From bbfed113e58d9b087f68321e6ab75be56f1c6e0a Mon Sep 17 00:00:00 2001 From: "fern-api[bot]" <115122769+fern-api[bot]@users.noreply.github.com> Date: Thu, 27 Aug 2026 22:32:16 +0000 Subject: [PATCH 1/3] [fern-generated] Update SDK Generated by Fern CLI Version: unknown Generators: - fernapi/fern-cli-generator: 0.38.6 --- .agents/skills/custom-commands/SKILL.md | 1 + .fern/replay.lock | 10 + .fernignore | 3 + .gitattributes | 1 + .github/workflows/ci.yml | 79 +- .github/workflows/release.yml | 17 +- Cargo.lock | 209 ++- Cargo.toml | 2 +- LICENSE | 202 --- README.md | 17 +- .../src/api/resources/api_keys/api_keys.rs | 294 +++ .../inboxes_browser_credentials.rs | 91 + .../inboxes/browser_credentials/mod.rs | 2 + .../src/api/resources/inboxes/mod.rs | 4 + agentmail-sdk/src/client.rs | 23 + agentmail-sdk/src/config.rs | 6 + agentmail-sdk/src/core/http_client.rs | 139 +- .../types/browser_authorization_list_limit.rs | 6 + agentmail-types/src/types/browser_consent.rs | 121 ++ .../src/types/browser_consent_client_type.rs | 17 + .../types/browser_consent_lifecycle_event.rs | 125 ++ ...ser_consent_lifecycle_event_client_type.rs | 17 + .../browser_consent_lifecycle_event_type.rs | 52 + .../src/types/browser_credential.rs | 109 ++ .../src/types/browser_credential_creator.rs | 59 + .../types/browser_credential_creator_kind.rs | 17 + .../src/types/browser_enrollment_accepted.rs | 60 + .../browser_enrollment_accepted_status.rs | 17 + .../browser_enrollment_lifecycle_event.rs | 116 ++ ...browser_enrollment_lifecycle_event_type.rs | 52 + .../browser_enrollment_transaction_jti.rs | 6 + .../src/types/browser_lifecycle_actor.rs | 59 + .../src/types/browser_lifecycle_actor_one.rs | 46 + .../types/browser_lifecycle_actor_one_type.rs | 17 + .../src/types/browser_lifecycle_actor_zero.rs | 46 + .../browser_lifecycle_actor_zero_type.rs | 17 + .../types/browser_lifecycle_api_key_actor.rs | 37 + .../browser_lifecycle_credential_actor.rs | 47 + .../src/types/browser_lifecycle_event.rs | 59 + .../create_browser_enrollment_request.rs | 38 + .../list_browser_consents_query_request.rs | 57 + .../types/list_browser_consents_response.rs | 66 + ...browser_credential_events_query_request.rs | 46 + .../list_browser_credentials_query_request.rs | 46 + .../list_browser_credentials_response.rs | 66 + .../list_browser_lifecycle_events_response.rs | 66 + agentmail-types/src/types/mod.rs | 62 +- agentmail-types/src/types/send_attachment.rs | 7 +- cli/agentmail/openapi0.json | 2 +- cli/agentmail/sdk.rs | 35 +- dist-workspace.toml | 2 +- reference.md | 104 ++ src/app.rs | 112 +- src/auth/compose.rs | 40 + src/auth/credential.rs | 146 +- src/auth/error.rs | 310 +++- src/auth/keyring_store.rs | 442 ++++- src/auth/login.rs | 11 +- src/auth/oauth_common.rs | 168 +- src/auth/oauth_login.rs | 832 ++++++++- src/auth/provider.rs | 15 + src/auth/schemes.rs | 26 +- src/cli_args.rs | 12 + src/error.rs | 1319 +++++++++++++- src/formatter.rs | 212 ++- src/graphql/executor.rs | 54 +- src/openapi/app.rs | 1314 +++++++++++++- src/openapi/binding.rs | 29 +- src/openapi/commands.rs | 1389 ++++++++++++-- src/openapi/discovery.rs | 81 + src/openapi/executor.rs | 318 +++- src/openapi/help.rs | 237 ++- src/openapi/parser.rs | 1613 ++++++++++++++++- src/openapi/skill_emitter.rs | 382 +++- src/sdk_executor.rs | 117 +- src/text.rs | 166 +- src/websocket/error.rs | 10 +- 77 files changed, 11100 insertions(+), 1054 deletions(-) create mode 100644 .fern/replay.lock create mode 100644 .gitattributes delete mode 100644 LICENSE create mode 100644 agentmail-sdk/src/api/resources/inboxes/browser_credentials/inboxes_browser_credentials.rs create mode 100644 agentmail-sdk/src/api/resources/inboxes/browser_credentials/mod.rs create mode 100644 agentmail-types/src/types/browser_authorization_list_limit.rs create mode 100644 agentmail-types/src/types/browser_consent.rs create mode 100644 agentmail-types/src/types/browser_consent_client_type.rs create mode 100644 agentmail-types/src/types/browser_consent_lifecycle_event.rs create mode 100644 agentmail-types/src/types/browser_consent_lifecycle_event_client_type.rs create mode 100644 agentmail-types/src/types/browser_consent_lifecycle_event_type.rs create mode 100644 agentmail-types/src/types/browser_credential.rs create mode 100644 agentmail-types/src/types/browser_credential_creator.rs create mode 100644 agentmail-types/src/types/browser_credential_creator_kind.rs create mode 100644 agentmail-types/src/types/browser_enrollment_accepted.rs create mode 100644 agentmail-types/src/types/browser_enrollment_accepted_status.rs create mode 100644 agentmail-types/src/types/browser_enrollment_lifecycle_event.rs create mode 100644 agentmail-types/src/types/browser_enrollment_lifecycle_event_type.rs create mode 100644 agentmail-types/src/types/browser_enrollment_transaction_jti.rs create mode 100644 agentmail-types/src/types/browser_lifecycle_actor.rs create mode 100644 agentmail-types/src/types/browser_lifecycle_actor_one.rs create mode 100644 agentmail-types/src/types/browser_lifecycle_actor_one_type.rs create mode 100644 agentmail-types/src/types/browser_lifecycle_actor_zero.rs create mode 100644 agentmail-types/src/types/browser_lifecycle_actor_zero_type.rs create mode 100644 agentmail-types/src/types/browser_lifecycle_api_key_actor.rs create mode 100644 agentmail-types/src/types/browser_lifecycle_credential_actor.rs create mode 100644 agentmail-types/src/types/browser_lifecycle_event.rs create mode 100644 agentmail-types/src/types/create_browser_enrollment_request.rs create mode 100644 agentmail-types/src/types/list_browser_consents_query_request.rs create mode 100644 agentmail-types/src/types/list_browser_consents_response.rs create mode 100644 agentmail-types/src/types/list_browser_credential_events_query_request.rs create mode 100644 agentmail-types/src/types/list_browser_credentials_query_request.rs create mode 100644 agentmail-types/src/types/list_browser_credentials_response.rs create mode 100644 agentmail-types/src/types/list_browser_lifecycle_events_response.rs diff --git a/.agents/skills/custom-commands/SKILL.md b/.agents/skills/custom-commands/SKILL.md index 8bd7800..f6b4e61 100644 --- a/.agents/skills/custom-commands/SKILL.md +++ b/.agents/skills/custom-commands/SKILL.md @@ -67,6 +67,7 @@ with the following sub-clients: |-------|------|-------------| | `client.inboxes` | `agentmail_sdk::api::InboxesClient` | inboxes operations | | `client.api_keys` | `agentmail_sdk::api::ApiKeysClient2` | api_keys operations | +| `client.browser_credentials` | `agentmail_sdk::api::BrowserCredentialsClient` | browser_credentials operations | | `client.drafts` | `agentmail_sdk::api::DraftsClient2` | drafts operations | | `client.events` | `agentmail_sdk::api::EventsClient` | events operations | | `client.lists` | `agentmail_sdk::api::ListsClient2` | lists operations | diff --git a/.fern/replay.lock b/.fern/replay.lock new file mode 100644 index 0000000..5f0f039 --- /dev/null +++ b/.fern/replay.lock @@ -0,0 +1,10 @@ +# DO NOT EDIT MANUALLY - Managed by Fern Replay +version: "1.0" +generations: + - commit_sha: 9fdec3aebc421f099dc93f1376545c86c5bdddc5 + tree_hash: ecd2ade331d1bcc4d9a9f7662675da2eba3dcd53 + timestamp: 2026-08-27T22:32:13.747Z + cli_version: unknown + generator_versions: {} +current_generation: 9fdec3aebc421f099dc93f1376545c86c5bdddc5 +patches: [] diff --git a/.fernignore b/.fernignore index 782b839..7d47a44 100644 --- a/.fernignore +++ b/.fernignore @@ -10,3 +10,6 @@ SECURITY.md # Hand-authored custom command bindings. The scaffold and its docs say # this file is protected; without this entry a regeneration overwrites it. cli/agentmail/custom.rs +.fern/replay.lock +.fern/replay.yml +.gitattributes diff --git a/.gitattributes b/.gitattributes new file mode 100644 index 0000000..74928d6 --- /dev/null +++ b/.gitattributes @@ -0,0 +1 @@ +.fern/replay.lock linguist-generated=true diff --git a/.github/workflows/ci.yml b/.github/workflows/ci.yml index 717328f..8e6e8af 100644 --- a/.github/workflows/ci.yml +++ b/.github/workflows/ci.yml @@ -112,6 +112,9 @@ jobs: - rust-target: aarch64-apple-darwin runner: macos-latest npm-platform-suffix: darwin-arm64 + - rust-target: x86_64-pc-windows-msvc + runner: windows-latest + npm-platform-suffix: win32-x64 steps: - name: Checkout repo uses: actions/checkout@v6 @@ -141,6 +144,12 @@ jobs: # that needs /lib/ld-musl-*.so.1 at runtime — which defeats the point # of a musl build and crashes where that loader is absent. Left alone, # rustc links the self-contained musl objects statically. + # + # Built with the `dist` profile — the same one cargo-dist uses for the + # GitHub Release — so npm and the Release ship byte-identical binaries + # for a given tag. Building `--release` here meant two channels + # shipping different bytes under one version, which surfaces as an + # unreproducible bug report. - name: Build release binary shell: bash run: | @@ -148,7 +157,7 @@ jobs: TARGET_UNDERSCORE=$(echo "${{ matrix.rust-target }}" | tr '-' '_') export "CC_${TARGET_UNDERSCORE}=musl-gcc" fi - cargo build --release --target ${{ matrix.rust-target }} + cargo build --profile dist --target ${{ matrix.rust-target }} - name: Package and publish npm platform package shell: bash @@ -165,7 +174,9 @@ jobs: if [[ "${{ matrix.rust-target }}" == *"windows"* ]]; then BINARY_NAME="agentmail.exe" fi - cp "target/${{ matrix.rust-target }}/release/${BINARY_NAME}" "${PKG_DIR}/" + # A custom cargo profile writes to target///, not + # .../release/. + cp "target/${{ matrix.rust-target }}/dist/${BINARY_NAME}" "${PKG_DIR}/" # Write platform package.json cat > "${PKG_DIR}/package.json" < rc + # and -next.1 -> next; an all-numeric or empty identifier falls + # back to "prerelease" because npm rejects a numeric dist-tag. + PRERELEASE="${VERSION#*-}" + if [[ "${VERSION}" == *-* ]]; then + TAG=$(echo "${PRERELEASE%%.*}" | tr -cd '[:alnum:]-') + if [[ -z "${TAG}" || "${TAG}" =~ ^[0-9]+$ ]]; then + TAG=prerelease + fi + echo "Publishing pre-release ${VERSION} with --tag ${TAG}" + npm publish --access public --tag "${TAG}" else PKG_NAME=$(node -p "require('./package.json').name") PKG_VERSION=$(node -p "require('./package.json').version") @@ -238,7 +257,8 @@ jobs: OPTIONAL_DEPS="${OPTIONAL_DEPS}\"agentmail-cli-linux-x64\": \"${VERSION}\"," OPTIONAL_DEPS="${OPTIONAL_DEPS}\"agentmail-cli-linux-arm64\": \"${VERSION}\"," OPTIONAL_DEPS="${OPTIONAL_DEPS}\"agentmail-cli-darwin-x64\": \"${VERSION}\"," - OPTIONAL_DEPS="${OPTIONAL_DEPS}\"agentmail-cli-darwin-arm64\": \"${VERSION}\"" + OPTIONAL_DEPS="${OPTIONAL_DEPS}\"agentmail-cli-darwin-arm64\": \"${VERSION}\"," + OPTIONAL_DEPS="${OPTIONAL_DEPS}\"agentmail-cli-win32-x64\": \"${VERSION}\"" cat > "${PKG_DIR}/package.json" < rc + # and -next.1 -> next; an all-numeric or empty identifier falls + # back to "prerelease" because npm rejects a numeric dist-tag. + PRERELEASE="${VERSION#*-}" + if [[ "${VERSION}" == *-* ]]; then + TAG=$(echo "${PRERELEASE%%.*}" | tr -cd '[:alnum:]-') + if [[ -z "${TAG}" || "${TAG}" =~ ^[0-9]+$ ]]; then + TAG=prerelease + fi + echo "Publishing pre-release ${VERSION} with --tag ${TAG}" + npm publish --access public --tag "${TAG}" else PKG_NAME=$(node -p "require('./package.json').name") PKG_VERSION=$(node -p "require('./package.json').version") diff --git a/.github/workflows/release.yml b/.github/workflows/release.yml index 9c0264b..378af83 100644 --- a/.github/workflows/release.yml +++ b/.github/workflows/release.yml @@ -315,7 +315,22 @@ jobs: # Write and read notes from a file to avoid quoting breaking things echo "$ANNOUNCEMENT_BODY" > $RUNNER_TEMP/notes.txt - gh release create "${{ needs.plan.outputs.tag }}" --target "$RELEASE_COMMIT" $PRERELEASE_FLAG --title "$ANNOUNCEMENT_TITLE" --notes-file "$RUNNER_TEMP/notes.txt" artifacts/* + # Re-runs and pre-created releases must not fail after a successful build. + # --draft=false publishes a release that was drafted in the UI: + # gh release view finds drafts, so without it the assets upload into + # a release nobody can see and the job still reports success. A draft + # has no tag yet, so --target decides where that tag lands; for an + # already-published release the tag exists and GitHub ignores it. + if gh release view "${{ needs.plan.outputs.tag }}" > /dev/null 2>&1; then + if [[ -n "$PRERELEASE_FLAG" ]]; then + gh release edit "${{ needs.plan.outputs.tag }}" --title "$ANNOUNCEMENT_TITLE" --target "$RELEASE_COMMIT" --notes-file "$RUNNER_TEMP/notes.txt" --draft=false --prerelease + else + gh release edit "${{ needs.plan.outputs.tag }}" --title "$ANNOUNCEMENT_TITLE" --target "$RELEASE_COMMIT" --notes-file "$RUNNER_TEMP/notes.txt" --draft=false --prerelease=false + fi + gh release upload "${{ needs.plan.outputs.tag }}" artifacts/* --clobber + else + gh release create "${{ needs.plan.outputs.tag }}" --target "$RELEASE_COMMIT" $PRERELEASE_FLAG --title "$ANNOUNCEMENT_TITLE" --notes-file "$RUNNER_TEMP/notes.txt" artifacts/* + fi announce: needs: diff --git a/Cargo.lock b/Cargo.lock index 3797534..e40b666 100644 --- a/Cargo.lock +++ b/Cargo.lock @@ -8,81 +8,6 @@ version = "2.0.1" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "320119579fcad9c21884f5c4861d16174d0e06250625266f50fe6898340abefa" -[[package]] -name = "agentmail-cli" -version = "1.0.0" -dependencies = [ - "agentmail_sdk", - "anyhow", - "base64", - "bytes", - "chrono", - "clap", - "clap_complete", - "clap_mangen", - "dotenvy", - "form_urlencoded", - "futures-util", - "hmac", - "httpdate", - "jmespath", - "keyring", - "libc", - "num-bigint", - "ordered-float", - "percent-encoding", - "pin-project", - "rand 0.8.6", - "reqwest", - "reqwest-sse", - "secrecy", - "serde", - "serde_json", - "serde_json_path", - "serde_qs", - "serde_yaml", - "serial_test", - "sha2", - "tempfile", - "thiserror 2.0.18", - "tokio", - "tokio-tungstenite", - "tokio-util", - "tracing", - "tracing-appender", - "tracing-subscriber", - "unicode-normalization", - "webbrowser", - "wiremock", -] - -[[package]] -name = "agentmail_sdk" -version = "0.1.0" -dependencies = [ - "agentmail_types", - "bytes", - "chrono", - "futures", - "reqwest", - "serde", - "serde_json", - "thiserror 1.0.69", - "tokio", -] - -[[package]] -name = "agentmail_types" -version = "0.0.0" -dependencies = [ - "base64", - "chrono", - "num-bigint", - "ordered-float", - "serde", - "serde_json", -] - [[package]] name = "aho-corasick" version = "1.1.4" @@ -137,7 +62,7 @@ version = "1.1.5" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "40c48f72fd53cd289104fc64099abca73db4166ad86ea0b4341abe65af83dadc" dependencies = [ - "windows-sys 0.61.2", + "windows-sys 0.60.2", ] [[package]] @@ -148,7 +73,7 @@ checksum = "291e6a250ff86cd4a820112fb8898808a366d8f9f58ce16d1f538353ad55747d" dependencies = [ "anstyle", "once_cell_polyfill", - "windows-sys 0.61.2", + "windows-sys 0.60.2", ] [[package]] @@ -518,6 +443,16 @@ dependencies = [ "subtle", ] +[[package]] +name = "dispatch2" +version = "0.3.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "1e0e367e4e7da84520dedcac1901e4da967309406d1e51017ae1abfb97adbd38" +dependencies = [ + "bitflags", + "objc2", +] + [[package]] name = "displaydoc" version = "0.2.6" @@ -548,7 +483,7 @@ source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "39cab71617ae0d63f51a36d69f866391735b51691dbda63cf6f96d042b63efeb" dependencies = [ "libc", - "windows-sys 0.61.2", + "windows-sys 0.52.0", ] [[package]] @@ -557,6 +492,54 @@ version = "2.4.1" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "9f1f227452a390804cdb637b74a86990f2a7d7ba4b7d5693aac9b4dd6defd8d6" +[[package]] +name = "agentmail-cli" +version = "1.1.0" +dependencies = [ + "agentmail_sdk", + "anyhow", + "base64", + "bytes", + "chrono", + "clap_complete", + "clap_mangen", + "clap", + "dotenvy", + "form_urlencoded", + "futures-util", + "hmac", + "httpdate", + "jmespath", + "keyring", + "libc", + "num-bigint", + "ordered-float", + "percent-encoding", + "pin-project", + "rand 0.8.6", + "reqwest-sse", + "reqwest", + "secrecy", + "serde_json_path", + "serde_json", + "serde_qs", + "serde_yaml", + "serde", + "serial_test", + "sha2", + "tempfile", + "thiserror 2.0.18", + "tokio-tungstenite", + "tokio-util", + "tokio", + "tracing-appender", + "tracing-subscriber", + "tracing", + "unicode-normalization", + "webbrowser", + "wiremock", +] + [[package]] name = "find-msvc-tools" version = "0.1.9" @@ -741,9 +724,9 @@ dependencies = [ [[package]] name = "h2" -version = "0.4.15" +version = "0.4.16" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "6cb093c84e8bd9b188d4c4a8cb6579fc016968d14c99882163cd3ff402a4f155" +checksum = "a9f37a958b41b3b19ee2707c06439c0e9e547e847223eb791ecb0cb821c65e27" dependencies = [ "atomic-waker", "bytes", @@ -1311,7 +1294,7 @@ version = "0.50.3" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "7957b9740744892f114936ab4a57b3f487491bbeafaf8083688b16841a4240e5" dependencies = [ - "windows-sys 0.61.2", + "windows-sys 0.60.2", ] [[package]] @@ -1368,6 +1351,28 @@ dependencies = [ "objc2-encode", ] +[[package]] +name = "objc2-app-kit" +version = "0.3.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "d49e936b501e5c5bf01fda3a9452ff86dc3ea98ad5f283e1455153142d97518c" +dependencies = [ + "bitflags", + "objc2", + "objc2-foundation", +] + +[[package]] +name = "objc2-core-foundation" +version = "0.3.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "2a180dd8642fa45cdb7dd721cd4c11b1cadd4929ce112ebd8b9f5803cc79d536" +dependencies = [ + "bitflags", + "dispatch2", + "objc2", +] + [[package]] name = "objc2-encode" version = "4.1.0" @@ -1382,6 +1387,7 @@ checksum = "e3e0adef53c21f888deb4fa59fc59f7eb17404926ee8a6f59f5df0fd7f9f3272" dependencies = [ "bitflags", "objc2", + "objc2-core-foundation", ] [[package]] @@ -1833,7 +1839,7 @@ dependencies = [ "errno", "libc", "linux-raw-sys", - "windows-sys 0.61.2", + "windows-sys 0.52.0", ] [[package]] @@ -2223,7 +2229,7 @@ source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "52d1cfed4120b4d927bf7c0f86d2087a4a7d6027c906d9f9d525a80573b9be51" dependencies = [ "libc", - "windows-sys 0.61.2", + "windows-sys 0.60.2", ] [[package]] @@ -2291,7 +2297,7 @@ dependencies = [ "getrandom 0.4.3", "once_cell", "rustix", - "windows-sys 0.61.2", + "windows-sys 0.52.0", ] [[package]] @@ -2863,15 +2869,15 @@ dependencies = [ [[package]] name = "webbrowser" -version = "1.2.1" +version = "1.2.4" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "0fc95580916af1e68ff6a7be07446fc5db73ebf71cf092de939bbf5f7e189f72" +checksum = "62c35be770821a214dbc362fc26908c853e776c0004294d0b10b8a6bad582f94" dependencies = [ - "core-foundation 0.10.1", "jni", "log", "ndk-context", "objc2", + "objc2-app-kit", "objc2-foundation", "url", "web-sys", @@ -2883,7 +2889,7 @@ version = "0.1.11" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "c2a7b1c03c876122aa43f3020e6c3c3ee5c05081c9a00739faf7503aeba10d22" dependencies = [ - "windows-sys 0.61.2", + "windows-sys 0.52.0", ] [[package]] @@ -3258,3 +3264,30 @@ name = "zmij" version = "1.0.21" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "b8848ee67ecc8aedbaf3e4122217aff892639231befc6a1b58d29fff4c2cabaa" + +[[package]] +name = "agentmail_types" +version = "0.0.0" +dependencies = [ + "base64", + "chrono", + "num-bigint", + "ordered-float", + "serde", + "serde_json", +] + +[[package]] +name = "agentmail_sdk" +version = "0.1.0" +dependencies = [ + "agentmail_types", + "bytes", + "chrono", + "futures", + "reqwest", + "serde", + "serde_json", + "thiserror 1.0.69", + "tokio", +] diff --git a/Cargo.toml b/Cargo.toml index 9607486..952272a 100644 --- a/Cargo.toml +++ b/Cargo.toml @@ -1,6 +1,6 @@ [package] name = "agentmail-cli" -version = "1.0.0" +version = "1.1.0" edition = "2021" description = "Command-line interface for the AgentMail API. Send, receive, reply, and manage threaded email conversations from your terminal." license = "MIT" diff --git a/LICENSE b/LICENSE deleted file mode 100644 index d645695..0000000 --- a/LICENSE +++ /dev/null @@ -1,202 +0,0 @@ - - Apache License - Version 2.0, January 2004 - http://www.apache.org/licenses/ - - TERMS AND CONDITIONS FOR USE, REPRODUCTION, AND DISTRIBUTION - - 1. Definitions. - - "License" shall mean the terms and conditions for use, reproduction, - and distribution as defined by Sections 1 through 9 of this document. - - "Licensor" shall mean the copyright owner or entity authorized by - the copyright owner that is granting the License. - - "Legal Entity" shall mean the union of the acting entity and all - other entities that control, are controlled by, or are under common - control with that entity. For the purposes of this definition, - "control" means (i) the power, direct or indirect, to cause the - direction or management of such entity, whether by contract or - otherwise, or (ii) ownership of fifty percent (50%) or more of the - outstanding shares, or (iii) beneficial ownership of such entity. - - "You" (or "Your") shall mean an individual or Legal Entity - exercising permissions granted by this License. - - "Source" form shall mean the preferred form for making modifications, - including but not limited to software source code, documentation - source, and configuration files. - - "Object" form shall mean any form resulting from mechanical - transformation or translation of a Source form, including but - not limited to compiled object code, generated documentation, - and conversions to other media types. - - "Work" shall mean the work of authorship, whether in Source or - Object form, made available under the License, as indicated by a - copyright notice that is included in or attached to the work - (an example is provided in the Appendix below). - - "Derivative Works" shall mean any work, whether in Source or Object - form, that is based on (or derived from) the Work and for which the - editorial revisions, annotations, elaborations, or other modifications - represent, as a whole, an original work of authorship. For the purposes - of this License, Derivative Works shall not include works that remain - separable from, or merely link (or bind by name) to the interfaces of, - the Work and Derivative Works thereof. - - "Contribution" shall mean any work of authorship, including - the original version of the Work and any modifications or additions - to that Work or Derivative Works thereof, that is intentionally - submitted to Licensor for inclusion in the Work by the copyright owner - or by an individual or Legal Entity authorized to submit on behalf of - the copyright owner. For the purposes of this definition, "submitted" - means any form of electronic, verbal, or written communication sent - to the Licensor or its representatives, including but not limited to - communication on electronic mailing lists, source code control systems, - and issue tracking systems that are managed by, or on behalf of, the - Licensor for the purpose of discussing and improving the Work, but - excluding communication that is conspicuously marked or otherwise - designated in writing by the copyright owner as "Not a Contribution." - - "Contributor" shall mean Licensor and any individual or Legal Entity - on behalf of whom a Contribution has been received by Licensor and - subsequently incorporated within the Work. - - 2. Grant of Copyright License. Subject to the terms and conditions of - this License, each Contributor hereby grants to You a perpetual, - worldwide, non-exclusive, no-charge, royalty-free, irrevocable - copyright license to reproduce, prepare Derivative Works of, - publicly display, publicly perform, sublicense, and distribute the - Work and such Derivative Works in Source or Object form. - - 3. Grant of Patent License. Subject to the terms and conditions of - this License, each Contributor hereby grants to You a perpetual, - worldwide, non-exclusive, no-charge, royalty-free, irrevocable - (except as stated in this section) patent license to make, have made, - use, offer to sell, sell, import, and otherwise transfer the Work, - where such license applies only to those patent claims licensable - by such Contributor that are necessarily infringed by their - Contribution(s) alone or by combination of their Contribution(s) - with the Work to which such Contribution(s) was submitted. If You - institute patent litigation against any entity (including a - cross-claim or counterclaim in a lawsuit) alleging that the Work - or a Contribution incorporated within the Work constitutes direct - or contributory patent infringement, then any patent licenses - granted to You under this License for that Work shall terminate - as of the date such litigation is filed. - - 4. Redistribution. You may reproduce and distribute copies of the - Work or Derivative Works thereof in any medium, with or without - modifications, and in Source or Object form, provided that You - meet the following conditions: - - (a) You must give any other recipients of the Work or - Derivative Works a copy of this License; and - - (b) You must cause any modified files to carry prominent notices - stating that You changed the files; and - - (c) You must retain, in the Source form of any Derivative Works - that You distribute, all copyright, patent, trademark, and - attribution notices from the Source form of the Work, - excluding those notices that do not pertain to any part of - the Derivative Works; and - - (d) If the Work includes a "NOTICE" text file as part of its - distribution, then any Derivative Works that You distribute must - include a readable copy of the attribution notices contained - within such NOTICE file, excluding those notices that do not - pertain to any part of the Derivative Works, in at least one - of the following places: within a NOTICE text file distributed - as part of the Derivative Works; within the Source form or - documentation, if provided along with the Derivative Works; or, - within a display generated by the Derivative Works, if and - wherever such third-party notices normally appear. The contents - of the NOTICE file are for informational purposes only and - do not modify the License. You may add Your own attribution - notices within Derivative Works that You distribute, alongside - or as an addendum to the NOTICE text from the Work, provided - that such additional attribution notices cannot be construed - as modifying the License. - - You may add Your own copyright statement to Your modifications and - may provide additional or different license terms and conditions - for use, reproduction, or distribution of Your modifications, or - for any such Derivative Works as a whole, provided Your use, - reproduction, and distribution of the Work otherwise complies with - the conditions stated in this License. - - 5. Submission of Contributions. Unless You explicitly state otherwise, - any Contribution intentionally submitted for inclusion in the Work - by You to the Licensor shall be under the terms and conditions of - this License, without any additional terms or conditions. - Notwithstanding the above, nothing herein shall supersede or modify - the terms of any separate license agreement you may have executed - with Licensor regarding such Contributions. - - 6. Trademarks. This License does not grant permission to use the trade - names, trademarks, service marks, or product names of the Licensor, - except as required for reasonable and customary use in describing the - origin of the Work and reproducing the content of the NOTICE file. - - 7. Disclaimer of Warranty. Unless required by applicable law or - agreed to in writing, Licensor provides the Work (and each - Contributor provides its Contributions) on an "AS IS" BASIS, - WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or - implied, including, without limitation, any warranties or conditions - of TITLE, NON-INFRINGEMENT, MERCHANTABILITY, or FITNESS FOR A - PARTICULAR PURPOSE. You are solely responsible for determining the - appropriateness of using or redistributing the Work and assume any - risks associated with Your exercise of permissions under this License. - - 8. Limitation of Liability. In no event and under no legal theory, - whether in tort (including negligence), contract, or otherwise, - unless required by applicable law (such as deliberate and grossly - negligent acts) or agreed to in writing, shall any Contributor be - liable to You for damages, including any direct, indirect, special, - incidental, or consequential damages of any character arising as a - result of this License or out of the use or inability to use the - Work (including but not limited to damages for loss of goodwill, - work stoppage, computer failure or malfunction, or any and all - other commercial damages or losses), even if such Contributor - has been advised of the possibility of such damages. - - 9. Accepting Warranty or Additional Liability. While redistributing - the Work or Derivative Works thereof, You may choose to offer, - and charge a fee for, acceptance of support, warranty, indemnity, - or other liability obligations and/or rights consistent with this - License. However, in accepting such obligations, You may act only - on Your own behalf and on Your sole responsibility, not on behalf - of any other Contributor, and only if You agree to indemnify, - defend, and hold each Contributor harmless for any liability - incurred by, or claims asserted against, such Contributor by reason - of your accepting any such warranty or additional liability. - - END OF TERMS AND CONDITIONS - - APPENDIX: How to apply the Apache License to your work. - - To apply the Apache License to your work, attach the following - boilerplate notice, with the fields enclosed by brackets "[]" - replaced with your own identifying information. (Don't include - the brackets!) The text should be enclosed in the appropriate - comment syntax for the file format. We also recommend that a - file or class name and description of purpose be included on the - same "printed page" as the copyright notice for easier - identification within third-party archives. - - Copyright [yyyy] [name of copyright owner] - - Licensed under the Apache License, Version 2.0 (the "License"); - you may not use this file except in compliance with the License. - You may obtain a copy of the License at - - http://www.apache.org/licenses/LICENSE-2.0 - - Unless required by applicable law or agreed to in writing, software - distributed under the License is distributed on an "AS IS" BASIS, - WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - See the License for the specific language governing permissions and - limitations under the License. diff --git a/README.md b/README.md index 8bc284a..c91097d 100644 --- a/README.md +++ b/README.md @@ -1,5 +1,6 @@ # AgentMail CLI +[![fern shield](https://img.shields.io/badge/%F0%9F%8C%BF-CLI%20generated%20by%20Fern-brightgreen)](https://buildwithfern.com?utm_source=github&utm_medium=github&utm_campaign=readme&utm_source=https%3A%2F%2Fgithub.com%2Fagentmail-to%2Fagentmail-cli) [![npm shield](https://img.shields.io/npm/v/agentmail-cli)](https://www.npmjs.com/package/agentmail-cli) Command-line interface for the AgentMail API. @@ -16,6 +17,7 @@ Command-line interface for the AgentMail API. - [Environment variables](#environment-variables) - [Output formats](#output-formats) - [Shell completion](#shell-completion) +- [Attribution](#attribution) ## Installation @@ -125,14 +127,19 @@ Standard environment variables (`HTTPS_PROXY` / `HTTP_PROXY` / `NO_PROXY` / `SSL ### Output formats -Use the global `--format` flag to control output. Supported values: `json` (default), `table`, `yaml`, `csv`. +Use the global `--format` flag to control output. Supported values: `json`, `table`, `yaml`, `csv`, `jsonl`, `raw`, `http`. + +Without `--format`, output (including errors) is `table` when stdout is a terminal and `json` when it is piped or redirected — so scripts and agents get JSON by default. Pass `--human` to keep the interactive rendering when piping to a pager, and `--format json` to pin JSON in a terminal. ```bash # Pipe JSON output through jq agentmail --format json | jq -# Machine-readable catalog of every operation -agentmail --help --format json | jq 'length' +# Keep the human rendering even when piped +agentmail --human | less + +# Machine-readable catalog of every operation (same as --schema) +agentmail --help --format json | jq '.operations | length' ``` ### Shell completion @@ -143,3 +150,7 @@ Generate shell completion scripts: agentmail completion ``` +## Attribution + +Built on [fern-cli-sdk](https://github.com/fern-api/fern), Copyright Fern, licensed under the [Apache License 2.0](https://www.apache.org/licenses/LICENSE-2.0). + diff --git a/agentmail-sdk/src/api/resources/api_keys/api_keys.rs b/agentmail-sdk/src/api/resources/api_keys/api_keys.rs index 1c2ff4a..8b0ac64 100644 --- a/agentmail-sdk/src/api/resources/api_keys/api_keys.rs +++ b/agentmail-sdk/src/api/resources/api_keys/api_keys.rs @@ -435,4 +435,298 @@ impl ApiKeysClient { ) .await } + + /// List active browser credentials visible to the caller's scope. Requires `api_key_read`. + /// + /// # Arguments + /// + /// * `options` - Additional request options such as headers, timeout, etc. + /// + /// # Returns + /// + /// JSON response from the API + /// + /// # Examples + /// + /// ```no_run + /// use agentmail_sdk::prelude::*; + /// + /// #[tokio::main] + /// async fn main() { + /// let config = ClientConfig { + /// token: Some("".to_string()), + /// ..Default::default() + /// }; + /// let client = AgentmailClient::new(config).expect("Failed to build client"); + /// client + /// .api_keys + /// .list_browser_credentials( + /// &ListBrowserCredentialsQueryRequest { + /// ..Default::default() + /// }, + /// None, + /// ) + /// .await; + /// } + /// ``` + pub async fn list_browser_credentials( + &self, + request: &ListBrowserCredentialsQueryRequest, + options: Option, + ) -> Result { + self.http_client + .execute_request( + Method::GET, + "v0/api-keys/browser-credentials", + None, + QueryBuilder::new() + .serialize("limit", request.limit.clone()) + .serialize("page_token", request.page_token.clone()) + .build(), + options, + ) + .await + } + + /// List owner-facing browser credential and consent lifecycle events. Requires `api_key_read`. + /// + /// # Arguments + /// + /// * `options` - Additional request options such as headers, timeout, etc. + /// + /// # Returns + /// + /// JSON response from the API + /// + /// # Examples + /// + /// ```no_run + /// use agentmail_sdk::prelude::*; + /// + /// #[tokio::main] + /// async fn main() { + /// let config = ClientConfig { + /// token: Some("".to_string()), + /// ..Default::default() + /// }; + /// let client = AgentmailClient::new(config).expect("Failed to build client"); + /// client + /// .api_keys + /// .list_browser_credential_events( + /// &ListBrowserCredentialEventsQueryRequest { + /// ..Default::default() + /// }, + /// None, + /// ) + /// .await; + /// } + /// ``` + pub async fn list_browser_credential_events( + &self, + request: &ListBrowserCredentialEventsQueryRequest, + options: Option, + ) -> Result { + self.http_client + .execute_request( + Method::GET, + "v0/api-keys/browser-credentials/events", + None, + QueryBuilder::new() + .serialize("limit", request.limit.clone()) + .serialize("page_token", request.page_token.clone()) + .build(), + options, + ) + .await + } + + /// Permanently revoke one active browser credential. Requires `api_key_delete`. + /// + /// # Arguments + /// + /// * `options` - Additional request options such as headers, timeout, etc. + /// + /// # Returns + /// + /// Empty response + /// + /// # Examples + /// + /// ```no_run + /// use agentmail_sdk::prelude::*; + /// + /// #[tokio::main] + /// async fn main() { + /// let config = ClientConfig { + /// token: Some("".to_string()), + /// ..Default::default() + /// }; + /// let client = AgentmailClient::new(config).expect("Failed to build client"); + /// client + /// .api_keys + /// .delete_browser_credential(&"credential_id".to_string(), None) + /// .await; + /// } + /// ``` + pub async fn delete_browser_credential( + &self, + credential_id: &str, + options: Option, + ) -> Result<(), ApiError> { + self.http_client + .execute_request( + Method::DELETE, + &format!("v0/api-keys/browser-credentials/{}", credential_id), + None, + None, + options, + ) + .await + } + + /// Cancel one pending, unexpired browser enrollment intent. Requires `api_key_delete`. + /// + /// # Arguments + /// + /// * `options` - Additional request options such as headers, timeout, etc. + /// + /// # Returns + /// + /// Empty response + /// + /// # Examples + /// + /// ```no_run + /// use agentmail_sdk::prelude::*; + /// + /// #[tokio::main] + /// async fn main() { + /// let config = ClientConfig { + /// token: Some("".to_string()), + /// ..Default::default() + /// }; + /// let client = AgentmailClient::new(config).expect("Failed to build client"); + /// client + /// .api_keys + /// .cancel_browser_enrollment(&"enrollment_id".to_string(), None) + /// .await; + /// } + /// ``` + pub async fn cancel_browser_enrollment( + &self, + enrollment_id: &str, + options: Option, + ) -> Result<(), ApiError> { + self.http_client + .execute_request( + Method::DELETE, + &format!( + "v0/api-keys/browser-credentials/enrollments/{}", + enrollment_id + ), + None, + None, + options, + ) + .await + } + + /// List remembered AgentID client approvals for one live inbox. Requires `api_key_read`. + /// + /// # Arguments + /// + /// * `options` - Additional request options such as headers, timeout, etc. + /// + /// # Returns + /// + /// JSON response from the API + /// + /// # Examples + /// + /// ```no_run + /// use agentmail_sdk::prelude::*; + /// + /// #[tokio::main] + /// async fn main() { + /// let config = ClientConfig { + /// token: Some("".to_string()), + /// ..Default::default() + /// }; + /// let client = AgentmailClient::new(config).expect("Failed to build client"); + /// client + /// .api_keys + /// .list_browser_consents( + /// &ListBrowserConsentsQueryRequest { + /// inbox_id: "inbox_id".to_string(), + /// limit: None, + /// page_token: None, + /// }, + /// None, + /// ) + /// .await; + /// } + /// ``` + pub async fn list_browser_consents( + &self, + request: &ListBrowserConsentsQueryRequest, + options: Option, + ) -> Result { + self.http_client + .execute_request( + Method::GET, + "v0/api-keys/browser-consents", + None, + QueryBuilder::new() + .string("inbox_id", request.inbox_id.clone()) + .serialize("limit", request.limit.clone()) + .serialize("page_token", request.page_token.clone()) + .build(), + options, + ) + .await + } + + /// Revoke one remembered AgentID client approval. Requires `api_key_delete`. + /// + /// # Arguments + /// + /// * `options` - Additional request options such as headers, timeout, etc. + /// + /// # Returns + /// + /// Empty response + /// + /// # Examples + /// + /// ```no_run + /// use agentmail_sdk::prelude::*; + /// + /// #[tokio::main] + /// async fn main() { + /// let config = ClientConfig { + /// token: Some("".to_string()), + /// ..Default::default() + /// }; + /// let client = AgentmailClient::new(config).expect("Failed to build client"); + /// client + /// .api_keys + /// .delete_browser_consent(&"consent_id".to_string(), None) + /// .await; + /// } + /// ``` + pub async fn delete_browser_consent( + &self, + consent_id: &str, + options: Option, + ) -> Result<(), ApiError> { + self.http_client + .execute_request( + Method::DELETE, + &format!("v0/api-keys/browser-consents/{}", consent_id), + None, + None, + options, + ) + .await + } } diff --git a/agentmail-sdk/src/api/resources/inboxes/browser_credentials/inboxes_browser_credentials.rs b/agentmail-sdk/src/api/resources/inboxes/browser_credentials/inboxes_browser_credentials.rs new file mode 100644 index 0000000..7f81d5d --- /dev/null +++ b/agentmail-sdk/src/api/resources/inboxes/browser_credentials/inboxes_browser_credentials.rs @@ -0,0 +1,91 @@ +use crate::api::*; +use crate::{ApiError, ClientConfig, HttpClient, RequestOptions}; +use reqwest::Method; + +pub struct BrowserCredentialsClient { + pub http_client: HttpClient, +} + +impl BrowserCredentialsClient { + pub fn new(config: ClientConfig) -> Result { + Ok(Self { + http_client: HttpClient::new(config.clone())?, + }) + } + + /// Attach a browser enrollment intent to the inbox. Requires + /// `api_key_create`. Before submitting `transaction_jti`, independently + /// verify that the browser page's final origin is exactly + /// `https://auth.agentid.com`. + /// + /// This endpoint is available to every organization using US production. + /// It is not available in EU production. + /// + /// Select `inbox_id` from trusted AgentMail configuration. An AgentID + /// `login_hint` is not authoritative for selecting the inbox; when the + /// transaction includes one, it must match the path inbox. + /// + /// **AgentMail API keys are sent only to `https://api.agentmail.to`; AgentID never requests them.** + /// + /// A new intent returns `202`; an idempotent retry for the same pending + /// transaction, inbox, and bearer key returns `200` with the same receipt. + /// An intent lasts at most five minutes. An activated credential lasts at + /// most 30 days and cannot outlive its authorizing bearer API key. + /// + /// Creation is limited to 20 intents per bearer API key per hour, 100 per + /// organization per hour, and five live unused intents per bearer API key. + /// Browser activation is separately limited to 20 activations per + /// authorizing bearer API key per UTC day. Either kind of limit can return + /// `429`; honor the `Retry-After` header. Cancelling an enrollment releases + /// its live-intent slot but does not reset the daily activation counter. + /// + /// # Arguments + /// + /// * `options` - Additional request options such as headers, timeout, etc. + /// + /// # Returns + /// + /// JSON response from the API + /// + /// # Examples + /// + /// ```no_run + /// use agentmail_sdk::prelude::*; + /// + /// #[tokio::main] + /// async fn main() { + /// let config = ClientConfig { + /// token: Some("".to_string()), + /// ..Default::default() + /// }; + /// let client = AgentmailClient::new(config).expect("Failed to build client"); + /// client + /// .inboxes + /// .browser_credentials + /// .create_enrollment( + /// &InboxesInboxID("inbox_id".to_string()), + /// &CreateBrowserEnrollmentRequest { + /// transaction_jti: BrowserEnrollmentTransactionJti("transaction_jti".to_string()), + /// }, + /// None, + /// ) + /// .await; + /// } + /// ``` + pub async fn create_enrollment( + &self, + inbox_id: &InboxesInboxId, + request: &CreateBrowserEnrollmentRequest, + options: Option, + ) -> Result { + self.http_client + .execute_request( + Method::POST, + &format!("v0/inboxes/{}/browser-credentials/enrollments", inbox_id.0), + Some(serde_json::to_value(request).map_err(ApiError::Serialization)?), + None, + options, + ) + .await + } +} diff --git a/agentmail-sdk/src/api/resources/inboxes/browser_credentials/mod.rs b/agentmail-sdk/src/api/resources/inboxes/browser_credentials/mod.rs new file mode 100644 index 0000000..78ab2e6 --- /dev/null +++ b/agentmail-sdk/src/api/resources/inboxes/browser_credentials/mod.rs @@ -0,0 +1,2 @@ +pub mod inboxes_browser_credentials; +pub use inboxes_browser_credentials::BrowserCredentialsClient; diff --git a/agentmail-sdk/src/api/resources/inboxes/mod.rs b/agentmail-sdk/src/api/resources/inboxes/mod.rs index cab821a..f2a23f5 100644 --- a/agentmail-sdk/src/api/resources/inboxes/mod.rs +++ b/agentmail-sdk/src/api/resources/inboxes/mod.rs @@ -4,6 +4,8 @@ use reqwest::Method; pub mod api_keys; pub use api_keys::ApiKeysClient2; +pub mod browser_credentials; +pub use browser_credentials::BrowserCredentialsClient; pub mod drafts; pub use drafts::DraftsClient2; pub mod events; @@ -21,6 +23,7 @@ pub use webhooks::WebhooksClient2; pub struct InboxesClient { pub http_client: HttpClient, pub api_keys: ApiKeysClient2, + pub browser_credentials: BrowserCredentialsClient, pub drafts: DraftsClient2, pub events: EventsClient, pub lists: ListsClient2, @@ -35,6 +38,7 @@ impl InboxesClient { Ok(Self { http_client: HttpClient::new(config.clone())?, api_keys: ApiKeysClient2::new(config.clone())?, + browser_credentials: BrowserCredentialsClient::new(config.clone())?, drafts: DraftsClient2::new(config.clone())?, events: EventsClient::new(config.clone())?, lists: ListsClient2::new(config.clone())?, diff --git a/agentmail-sdk/src/client.rs b/agentmail-sdk/src/client.rs index d9907a3..c1ebb3f 100644 --- a/agentmail-sdk/src/client.rs +++ b/agentmail-sdk/src/client.rs @@ -98,6 +98,16 @@ impl ApiClientBuilder { self } + /// Use a custom `reqwest` client for every request + /// + /// The supplied client is used as-is, so it owns all transport-level + /// configuration — custom certificates, proxies, timeouts and user agent. + /// Authentication, custom headers and retries are still applied by the SDK. + pub fn reqwest_client(mut self, client: reqwest::Client) -> Self { + self.config.reqwest_client = Some(client); + self + } + /// Build the client with validation pub fn build(self) -> Result { ApiClient::new(self.config) @@ -223,6 +233,19 @@ mod tests { assert_eq!(builder.config.user_agent, "test/1.0"); } + #[test] + fn test_reqwest_client() { + let builder = + ApiClientBuilder::new("https://api.example.com").reqwest_client(reqwest::Client::new()); + assert!(builder.config.reqwest_client.is_some()); + } + + #[test] + fn test_reqwest_client_defaults_to_none() { + let builder = ApiClientBuilder::new("https://api.example.com"); + assert!(builder.config.reqwest_client.is_none()); + } + #[test] fn test_build_succeeds() { let result = ApiClientBuilder::new("https://api.example.com").build(); diff --git a/agentmail-sdk/src/config.rs b/agentmail-sdk/src/config.rs index f2ad191..6dfdac2 100644 --- a/agentmail-sdk/src/config.rs +++ b/agentmail-sdk/src/config.rs @@ -16,6 +16,11 @@ pub struct ClientConfig { pub max_retries: u32, pub custom_headers: HashMap, pub user_agent: String, + /// Optional custom `reqwest` client, used as-is for every request. + /// When set, it owns all transport-level configuration (TLS, proxies, timeout, + /// user agent); when `None` the SDK builds its own client from `timeout` and + /// `user_agent`. + pub reqwest_client: Option, } impl Default for ClientConfig { fn default() -> Self { @@ -37,6 +42,7 @@ impl Default for ClientConfig { ("X-Fern-SDK-Version".to_string(), "0.1.0".to_string()), ]), user_agent: "Api Rust SDK".to_string(), + reqwest_client: None, } } } diff --git a/agentmail-sdk/src/core/http_client.rs b/agentmail-sdk/src/core/http_client.rs index 7165db0..1dfaeba 100644 --- a/agentmail-sdk/src/core/http_client.rs +++ b/agentmail-sdk/src/core/http_client.rs @@ -106,7 +106,9 @@ impl Stream for ByteStream { /// /// When an external executor is provided, the SDK delegates raw HTTP /// execution to it, allowing the caller's transport stack to handle -/// auth, retries, and TLS configuration. +/// auth, retries, and TLS configuration. Custom headers configured on +/// the client or on the request options are still applied by the SDK +/// before the request reaches the executor. #[doc(hidden)] pub trait RequestExecutor: Send + Sync { fn execute( @@ -206,15 +208,22 @@ impl HttpClient { /// /// When `oauth_config` is provided, the client will automatically fetch and refresh /// OAuth tokens before making requests. + /// + /// When `config.reqwest_client` is set, that client is used as-is and owns all + /// transport-level settings (TLS, proxies, timeout, user agent); SDK-level auth, + /// custom headers and retries are still applied on top of it. pub fn new_with_oauth( config: ClientConfig, oauth_config: Option, ) -> Result { - let client = Client::builder() - .timeout(config.timeout) - .user_agent(&config.user_agent) - .build() - .map_err(ApiError::Network)?; + let client = match config.reqwest_client.clone() { + Some(client) => client, + None => Client::builder() + .timeout(config.timeout) + .user_agent(&config.user_agent) + .build() + .map_err(ApiError::Network)?, + }; Ok(Self { client, @@ -227,10 +236,12 @@ impl HttpClient { /// Creates an HttpClient with an injected request executor. /// /// When using an injected executor, the client delegates HTTP execution - /// entirely to the executor. Auth headers, custom headers, and retry - /// logic are NOT applied by this client — the executor's transport - /// stack is expected to handle them. This prevents double-retry and - /// double-auth when the SDK is embedded inside a CLI. + /// entirely to the executor. Auth headers and retry logic are NOT applied + /// by this client — the executor's transport stack is expected to handle + /// them. This prevents double-retry and double-auth when the SDK is + /// embedded inside a CLI. Custom headers (from `ClientConfig` and from + /// `RequestOptions`) are still applied, since the executor has no way to + /// know about them. #[doc(hidden)] pub fn with_executor(executor: Arc, config: ClientConfig) -> Self { let client = Client::new(); @@ -366,14 +377,16 @@ impl HttpClient { } /// Applies auth/headers and executes the request, choosing between - /// the injected executor path (no SDK-level auth/headers/retries) - /// and the default path (full SDK behavior). + /// the injected executor path (custom headers only, no SDK-level + /// auth/retries) and the default path (full SDK behavior). async fn send_request( &self, req: Request, options: &Option, ) -> Result { if let Some(executor) = &self.executor { + let mut req = req; + self.apply_custom_headers(&mut req, options)?; executor.execute(req).await.map_err(ApiError::Executor) } else { let mut req = req; @@ -1013,6 +1026,43 @@ mod tests { ); } + #[tokio::test] + async fn test_supplied_reqwest_client_is_used_and_keeps_sdk_headers() { + let listener = tokio::net::TcpListener::bind("127.0.0.1:0") + .await + .expect("bind"); + let base_url = format!("http://{}", listener.local_addr().expect("addr")); + // Reuses the raw-capture server above; its token-shaped body is irrelevant here, + // only the request it echoes back matters. + let server = tokio::spawn(serve_one_token_request(listener)); + + let config = ClientConfig { + base_url, + reqwest_client: Some( + Client::builder() + .user_agent("custom-transport/1.0") + .build() + .expect("build custom client"), + ), + ..Default::default() + }; + let client = HttpClient::new(config).expect("client"); + + let _: Result = client + .execute_request(Method::GET, "/ping", None, None, None) + .await; + + let raw_request = server.await.expect("server"); + assert!( + raw_request.contains("user-agent: custom-transport/1.0"), + "the supplied reqwest client must execute the request: {raw_request}" + ); + assert!( + raw_request.contains("x-fern-language: Rust"), + "SDK-level headers must still be applied to a custom client: {raw_request}" + ); + } + #[tokio::test] async fn test_oauth_token_request_form_encodes_when_configured() { let listener = tokio::net::TcpListener::bind("127.0.0.1:0") @@ -1051,4 +1101,69 @@ mod tests { "token request should not send a JSON content type when form-encoded: {raw_request}" ); } + + /// Captures the request it is handed and refuses to send it, so the + /// headers the SDK applied before delegating are observable. + struct RecordingExecutor { + seen: std::sync::Mutex>, + } + + impl RequestExecutor for RecordingExecutor { + fn execute( + &self, + request: Request, + ) -> BoxFuture<'_, Result>> { + *self.seen.lock().expect("lock") = Some(request.headers().clone()); + Box::pin(async { Err("not sent".into()) }) + } + } + + #[tokio::test] + async fn test_executor_path_applies_custom_headers() { + // Regression: the executor branch delegated the request untouched, so + // headers only the SDK knows about — `ClientConfig::custom_headers` + // (e.g. an `X-Source` override, plus the X-Fern-* platform headers) + // and `RequestOptions::additional_headers` — never reached the wire. + // An injected executor (the generated CLI's custom-command path) + // cannot recover them on its own. + let executor = Arc::new(RecordingExecutor { + seen: std::sync::Mutex::new(None), + }); + let mut config = ClientConfig::default(); + config + .custom_headers + .insert("X-Source".to_string(), "custom-command".to_string()); + let client = HttpClient::with_executor(executor.clone(), config); + + let request = Client::new() + .get("http://127.0.0.1:1/ping") + .build() + .expect("build request"); + let options = RequestOptions::new().additional_header("X-Request-Level", "sunflower"); + let _: Result = client.send_request(request, &Some(options)).await; + + let seen = executor + .seen + .lock() + .expect("lock") + .clone() + .expect("executor received the request"); + assert_eq!( + seen.get("x-source").map(|v| v.to_str().expect("utf-8")), + Some("custom-command"), + "client-level custom headers must survive the executor path: {seen:?}" + ); + assert_eq!( + seen.get("x-request-level") + .map(|v| v.to_str().expect("utf-8")), + Some("sunflower"), + "request-level headers must survive the executor path: {seen:?}" + ); + assert_eq!( + seen.get("x-fern-language") + .map(|v| v.to_str().expect("utf-8")), + Some("Rust"), + "SDK platform headers must survive the executor path: {seen:?}" + ); + } } diff --git a/agentmail-types/src/types/browser_authorization_list_limit.rs b/agentmail-types/src/types/browser_authorization_list_limit.rs new file mode 100644 index 0000000..65b055d --- /dev/null +++ b/agentmail-types/src/types/browser_authorization_list_limit.rs @@ -0,0 +1,6 @@ +pub use crate::prelude::*; +#[allow(unused_imports)] +use super::*; + +#[derive(Debug, Clone, Serialize, Deserialize, PartialEq, Eq, Hash, Default)] +pub struct BrowserAuthorizationListLimit(pub i64); \ No newline at end of file diff --git a/agentmail-types/src/types/browser_consent.rs b/agentmail-types/src/types/browser_consent.rs new file mode 100644 index 0000000..4cdfb3d --- /dev/null +++ b/agentmail-types/src/types/browser_consent.rs @@ -0,0 +1,121 @@ +pub use crate::prelude::*; +#[allow(unused_imports)] +use super::*; + +/// Remembered approval for one closed AgentID client and inbox. +#[derive(Debug, Clone, Serialize, Deserialize, PartialEq, Eq, Hash)] +pub struct BrowserConsent { + #[serde(default)] + pub consent_id: String, + #[serde(default)] + pub inbox_id: String, + pub client_type: BrowserConsentClientType, + #[serde(default)] + pub client_id: String, + /// Registered client URL, when one is available. + #[serde(skip_serializing_if = "Option::is_none")] + pub client_url: Option, + /// At least one non-empty scope approved for this client. + #[serde(default)] + pub approved_scopes: Vec, + #[serde(default)] + #[serde(with = "crate::core::flexible_datetime::offset")] + pub created_at: DateTime, + #[serde(default)] + #[serde(with = "crate::core::flexible_datetime::offset")] + pub updated_at: DateTime, + #[serde(default)] + #[serde(with = "crate::core::flexible_datetime::offset")] + pub expires_at: DateTime, +} + +impl BrowserConsent { + pub fn builder() -> BrowserConsentBuilder { + ::default() + } +} + +#[derive(Clone, PartialEq, Default, Debug)] +#[non_exhaustive] +pub struct BrowserConsentBuilder { + consent_id: Option, + inbox_id: Option, + client_type: Option, + client_id: Option, + client_url: Option, + approved_scopes: Option>, + created_at: Option>, + updated_at: Option>, + expires_at: Option>, +} + +impl BrowserConsentBuilder { + pub fn consent_id(mut self, value: impl Into) -> Self { + self.consent_id = Some(value.into()); + self + } + + pub fn inbox_id(mut self, value: impl Into) -> Self { + self.inbox_id = Some(value.into()); + self + } + + pub fn client_type(mut self, value: BrowserConsentClientType) -> Self { + self.client_type = Some(value); + self + } + + pub fn client_id(mut self, value: impl Into) -> Self { + self.client_id = Some(value.into()); + self + } + + pub fn client_url(mut self, value: impl Into) -> Self { + self.client_url = Some(value.into()); + self + } + + pub fn approved_scopes(mut self, value: Vec) -> Self { + self.approved_scopes = Some(value); + self + } + + pub fn created_at(mut self, value: DateTime) -> Self { + self.created_at = Some(value); + self + } + + pub fn updated_at(mut self, value: DateTime) -> Self { + self.updated_at = Some(value); + self + } + + pub fn expires_at(mut self, value: DateTime) -> Self { + self.expires_at = Some(value); + self + } + + /// Consumes the builder and constructs a [`BrowserConsent`]. + /// This method will fail if any of the following fields are not set: + /// - [`consent_id`](BrowserConsentBuilder::consent_id) + /// - [`inbox_id`](BrowserConsentBuilder::inbox_id) + /// - [`client_type`](BrowserConsentBuilder::client_type) + /// - [`client_id`](BrowserConsentBuilder::client_id) + /// - [`approved_scopes`](BrowserConsentBuilder::approved_scopes) + /// - [`created_at`](BrowserConsentBuilder::created_at) + /// - [`updated_at`](BrowserConsentBuilder::updated_at) + /// - [`expires_at`](BrowserConsentBuilder::expires_at) + pub fn build(self) -> Result { + Ok(BrowserConsent { + consent_id: self.consent_id.ok_or_else(|| BuildError::missing_field("consent_id"))?, + inbox_id: self.inbox_id.ok_or_else(|| BuildError::missing_field("inbox_id"))?, + client_type: self.client_type.ok_or_else(|| BuildError::missing_field("client_type"))?, + client_id: self.client_id.ok_or_else(|| BuildError::missing_field("client_id"))?, + client_url: self.client_url, + approved_scopes: self.approved_scopes.ok_or_else(|| BuildError::missing_field("approved_scopes"))?, + created_at: self.created_at.ok_or_else(|| BuildError::missing_field("created_at"))?, + updated_at: self.updated_at.ok_or_else(|| BuildError::missing_field("updated_at"))?, + expires_at: self.expires_at.ok_or_else(|| BuildError::missing_field("expires_at"))?, + }) + } +} diff --git a/agentmail-types/src/types/browser_consent_client_type.rs b/agentmail-types/src/types/browser_consent_client_type.rs new file mode 100644 index 0000000..79b540b --- /dev/null +++ b/agentmail-types/src/types/browser_consent_client_type.rs @@ -0,0 +1,17 @@ +pub use crate::prelude::*; +#[allow(unused_imports)] +use super::*; + +#[derive(Debug, Clone, Serialize, Deserialize, PartialEq, Eq, Hash)] +pub enum BrowserConsentClientType { + #[serde(rename = "closed")] + Closed, +} +impl fmt::Display for BrowserConsentClientType { + fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result { + let s = match self { + Self::Closed => "closed", + }; + write!(f, "{}", s) + } +} diff --git a/agentmail-types/src/types/browser_consent_lifecycle_event.rs b/agentmail-types/src/types/browser_consent_lifecycle_event.rs new file mode 100644 index 0000000..7d533c1 --- /dev/null +++ b/agentmail-types/src/types/browser_consent_lifecycle_event.rs @@ -0,0 +1,125 @@ +pub use crate::prelude::*; +#[allow(unused_imports)] +use super::*; + +#[derive(Debug, Clone, Serialize, Deserialize, PartialEq, Eq, Hash)] +pub struct BrowserConsentLifecycleEvent { + pub r#type: BrowserConsentLifecycleEventType, + #[serde(default)] + pub trace_id: String, + #[serde(default)] + pub event_id: String, + #[serde(default)] + #[serde(with = "crate::core::flexible_datetime::offset")] + pub occurred_at: DateTime, + #[serde(default)] + pub organization_id: String, + #[serde(default)] + pub pod_id: String, + pub actor: BrowserLifecycleActor, + #[serde(default)] + pub consent_id: String, + pub client_type: BrowserConsentLifecycleEventClientType, + #[serde(default)] + pub client_id: String, +} + +impl BrowserConsentLifecycleEvent { + pub fn builder() -> BrowserConsentLifecycleEventBuilder { + ::default() + } +} + +#[derive(Clone, PartialEq, Default, Debug)] +#[non_exhaustive] +pub struct BrowserConsentLifecycleEventBuilder { + r#type: Option, + trace_id: Option, + event_id: Option, + occurred_at: Option>, + organization_id: Option, + pod_id: Option, + actor: Option, + consent_id: Option, + client_type: Option, + client_id: Option, +} + +impl BrowserConsentLifecycleEventBuilder { + pub fn r#type(mut self, value: BrowserConsentLifecycleEventType) -> Self { + self.r#type = Some(value); + self + } + + pub fn trace_id(mut self, value: impl Into) -> Self { + self.trace_id = Some(value.into()); + self + } + + pub fn event_id(mut self, value: impl Into) -> Self { + self.event_id = Some(value.into()); + self + } + + pub fn occurred_at(mut self, value: DateTime) -> Self { + self.occurred_at = Some(value); + self + } + + pub fn organization_id(mut self, value: impl Into) -> Self { + self.organization_id = Some(value.into()); + self + } + + pub fn pod_id(mut self, value: impl Into) -> Self { + self.pod_id = Some(value.into()); + self + } + + pub fn actor(mut self, value: BrowserLifecycleActor) -> Self { + self.actor = Some(value); + self + } + + pub fn consent_id(mut self, value: impl Into) -> Self { + self.consent_id = Some(value.into()); + self + } + + pub fn client_type(mut self, value: BrowserConsentLifecycleEventClientType) -> Self { + self.client_type = Some(value); + self + } + + pub fn client_id(mut self, value: impl Into) -> Self { + self.client_id = Some(value.into()); + self + } + + /// Consumes the builder and constructs a [`BrowserConsentLifecycleEvent`]. + /// This method will fail if any of the following fields are not set: + /// - [`r#type`](BrowserConsentLifecycleEventBuilder::r#type) + /// - [`trace_id`](BrowserConsentLifecycleEventBuilder::trace_id) + /// - [`event_id`](BrowserConsentLifecycleEventBuilder::event_id) + /// - [`occurred_at`](BrowserConsentLifecycleEventBuilder::occurred_at) + /// - [`organization_id`](BrowserConsentLifecycleEventBuilder::organization_id) + /// - [`pod_id`](BrowserConsentLifecycleEventBuilder::pod_id) + /// - [`actor`](BrowserConsentLifecycleEventBuilder::actor) + /// - [`consent_id`](BrowserConsentLifecycleEventBuilder::consent_id) + /// - [`client_type`](BrowserConsentLifecycleEventBuilder::client_type) + /// - [`client_id`](BrowserConsentLifecycleEventBuilder::client_id) + pub fn build(self) -> Result { + Ok(BrowserConsentLifecycleEvent { + r#type: self.r#type.ok_or_else(|| BuildError::missing_field("r#type"))?, + trace_id: self.trace_id.ok_or_else(|| BuildError::missing_field("trace_id"))?, + event_id: self.event_id.ok_or_else(|| BuildError::missing_field("event_id"))?, + occurred_at: self.occurred_at.ok_or_else(|| BuildError::missing_field("occurred_at"))?, + organization_id: self.organization_id.ok_or_else(|| BuildError::missing_field("organization_id"))?, + pod_id: self.pod_id.ok_or_else(|| BuildError::missing_field("pod_id"))?, + actor: self.actor.ok_or_else(|| BuildError::missing_field("actor"))?, + consent_id: self.consent_id.ok_or_else(|| BuildError::missing_field("consent_id"))?, + client_type: self.client_type.ok_or_else(|| BuildError::missing_field("client_type"))?, + client_id: self.client_id.ok_or_else(|| BuildError::missing_field("client_id"))?, + }) + } +} diff --git a/agentmail-types/src/types/browser_consent_lifecycle_event_client_type.rs b/agentmail-types/src/types/browser_consent_lifecycle_event_client_type.rs new file mode 100644 index 0000000..18876cf --- /dev/null +++ b/agentmail-types/src/types/browser_consent_lifecycle_event_client_type.rs @@ -0,0 +1,17 @@ +pub use crate::prelude::*; +#[allow(unused_imports)] +use super::*; + +#[derive(Debug, Clone, Serialize, Deserialize, PartialEq, Eq, Hash)] +pub enum BrowserConsentLifecycleEventClientType { + #[serde(rename = "closed")] + Closed, +} +impl fmt::Display for BrowserConsentLifecycleEventClientType { + fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result { + let s = match self { + Self::Closed => "closed", + }; + write!(f, "{}", s) + } +} diff --git a/agentmail-types/src/types/browser_consent_lifecycle_event_type.rs b/agentmail-types/src/types/browser_consent_lifecycle_event_type.rs new file mode 100644 index 0000000..928a930 --- /dev/null +++ b/agentmail-types/src/types/browser_consent_lifecycle_event_type.rs @@ -0,0 +1,52 @@ +pub use crate::prelude::*; +#[allow(unused_imports)] +use super::*; + +#[non_exhaustive] +#[derive(Debug, Clone, PartialEq, Eq, Hash)] +pub enum BrowserConsentLifecycleEventType { + BrowserConsentCreated, + BrowserConsentUpdated, + BrowserConsentReused, + BrowserConsentRevoked, + /// This variant is used for forward compatibility. + /// If the server sends a value not recognized by the current SDK version, + /// it will be captured here with the raw string value. + __Unknown(String), +} +impl Serialize for BrowserConsentLifecycleEventType { + fn serialize(&self, serializer: S) -> Result { + match self { + Self::BrowserConsentCreated => serializer.serialize_str("browser_consent_created"), + Self::BrowserConsentUpdated => serializer.serialize_str("browser_consent_updated"), + Self::BrowserConsentReused => serializer.serialize_str("browser_consent_reused"), + Self::BrowserConsentRevoked => serializer.serialize_str("browser_consent_revoked"), + Self::__Unknown(val) => serializer.serialize_str(val), + } + } +} + +impl<'de> Deserialize<'de> for BrowserConsentLifecycleEventType { + fn deserialize>(deserializer: D) -> Result { + let value = String::deserialize(deserializer)?; + match value.as_str() { + "browser_consent_created" => Ok(Self::BrowserConsentCreated), + "browser_consent_updated" => Ok(Self::BrowserConsentUpdated), + "browser_consent_reused" => Ok(Self::BrowserConsentReused), + "browser_consent_revoked" => Ok(Self::BrowserConsentRevoked), + _ => Ok(Self::__Unknown(value)), + } + } +} + +impl fmt::Display for BrowserConsentLifecycleEventType { + fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result { + match self { + Self::BrowserConsentCreated => write!(f, "browser_consent_created"), + Self::BrowserConsentUpdated => write!(f, "browser_consent_updated"), + Self::BrowserConsentReused => write!(f, "browser_consent_reused"), + Self::BrowserConsentRevoked => write!(f, "browser_consent_revoked"), + Self::__Unknown(val) => write!(f, "{}", val), + } + } +} diff --git a/agentmail-types/src/types/browser_credential.rs b/agentmail-types/src/types/browser_credential.rs new file mode 100644 index 0000000..1de1daf --- /dev/null +++ b/agentmail-types/src/types/browser_credential.rs @@ -0,0 +1,109 @@ +pub use crate::prelude::*; +#[allow(unused_imports)] +use super::*; + +/// Owner-facing metadata for an active browser credential. Private key material never leaves the browser. +#[derive(Debug, Clone, Serialize, Deserialize, PartialEq, Eq, Hash)] +pub struct BrowserCredential { + #[serde(default)] + pub credential_id: String, + #[serde(default)] + pub public_key_fingerprint_prefix: String, + #[serde(default)] + pub organization_id: String, + #[serde(default)] + pub pod_id: String, + #[serde(default)] + pub inbox_id: String, + pub created_by: BrowserCredentialCreator, + #[serde(default)] + #[serde(with = "crate::core::flexible_datetime::offset")] + pub created_at: DateTime, + #[serde(default)] + #[serde(with = "crate::core::flexible_datetime::offset")] + pub expires_at: DateTime, +} + +impl BrowserCredential { + pub fn builder() -> BrowserCredentialBuilder { + ::default() + } +} + +#[derive(Clone, PartialEq, Default, Debug)] +#[non_exhaustive] +pub struct BrowserCredentialBuilder { + credential_id: Option, + public_key_fingerprint_prefix: Option, + organization_id: Option, + pod_id: Option, + inbox_id: Option, + created_by: Option, + created_at: Option>, + expires_at: Option>, +} + +impl BrowserCredentialBuilder { + pub fn credential_id(mut self, value: impl Into) -> Self { + self.credential_id = Some(value.into()); + self + } + + pub fn public_key_fingerprint_prefix(mut self, value: impl Into) -> Self { + self.public_key_fingerprint_prefix = Some(value.into()); + self + } + + pub fn organization_id(mut self, value: impl Into) -> Self { + self.organization_id = Some(value.into()); + self + } + + pub fn pod_id(mut self, value: impl Into) -> Self { + self.pod_id = Some(value.into()); + self + } + + pub fn inbox_id(mut self, value: impl Into) -> Self { + self.inbox_id = Some(value.into()); + self + } + + pub fn created_by(mut self, value: BrowserCredentialCreator) -> Self { + self.created_by = Some(value); + self + } + + pub fn created_at(mut self, value: DateTime) -> Self { + self.created_at = Some(value); + self + } + + pub fn expires_at(mut self, value: DateTime) -> Self { + self.expires_at = Some(value); + self + } + + /// Consumes the builder and constructs a [`BrowserCredential`]. + /// This method will fail if any of the following fields are not set: + /// - [`credential_id`](BrowserCredentialBuilder::credential_id) + /// - [`public_key_fingerprint_prefix`](BrowserCredentialBuilder::public_key_fingerprint_prefix) + /// - [`organization_id`](BrowserCredentialBuilder::organization_id) + /// - [`pod_id`](BrowserCredentialBuilder::pod_id) + /// - [`inbox_id`](BrowserCredentialBuilder::inbox_id) + /// - [`created_by`](BrowserCredentialBuilder::created_by) + /// - [`created_at`](BrowserCredentialBuilder::created_at) + /// - [`expires_at`](BrowserCredentialBuilder::expires_at) + pub fn build(self) -> Result { + Ok(BrowserCredential { + credential_id: self.credential_id.ok_or_else(|| BuildError::missing_field("credential_id"))?, + public_key_fingerprint_prefix: self.public_key_fingerprint_prefix.ok_or_else(|| BuildError::missing_field("public_key_fingerprint_prefix"))?, + organization_id: self.organization_id.ok_or_else(|| BuildError::missing_field("organization_id"))?, + pod_id: self.pod_id.ok_or_else(|| BuildError::missing_field("pod_id"))?, + inbox_id: self.inbox_id.ok_or_else(|| BuildError::missing_field("inbox_id"))?, + created_by: self.created_by.ok_or_else(|| BuildError::missing_field("created_by"))?, + created_at: self.created_at.ok_or_else(|| BuildError::missing_field("created_at"))?, + expires_at: self.expires_at.ok_or_else(|| BuildError::missing_field("expires_at"))?, + }) + } +} diff --git a/agentmail-types/src/types/browser_credential_creator.rs b/agentmail-types/src/types/browser_credential_creator.rs new file mode 100644 index 0000000..4e47547 --- /dev/null +++ b/agentmail-types/src/types/browser_credential_creator.rs @@ -0,0 +1,59 @@ +pub use crate::prelude::*; +#[allow(unused_imports)] +use super::*; + +#[derive(Debug, Clone, Serialize, Deserialize, PartialEq, Eq, Hash)] +pub struct BrowserCredentialCreator { + pub kind: BrowserCredentialCreatorKind, + /// Bearer API key that authorized creation of the browser credential. + #[serde(default)] + pub api_key_id: String, + /// Incarnation timestamp of the authorizing bearer API key. + #[serde(default)] + #[serde(with = "crate::core::flexible_datetime::offset")] + pub created_at: DateTime, +} + +impl BrowserCredentialCreator { + pub fn builder() -> BrowserCredentialCreatorBuilder { + ::default() + } +} + +#[derive(Clone, PartialEq, Default, Debug)] +#[non_exhaustive] +pub struct BrowserCredentialCreatorBuilder { + kind: Option, + api_key_id: Option, + created_at: Option>, +} + +impl BrowserCredentialCreatorBuilder { + pub fn kind(mut self, value: BrowserCredentialCreatorKind) -> Self { + self.kind = Some(value); + self + } + + pub fn api_key_id(mut self, value: impl Into) -> Self { + self.api_key_id = Some(value.into()); + self + } + + pub fn created_at(mut self, value: DateTime) -> Self { + self.created_at = Some(value); + self + } + + /// Consumes the builder and constructs a [`BrowserCredentialCreator`]. + /// This method will fail if any of the following fields are not set: + /// - [`kind`](BrowserCredentialCreatorBuilder::kind) + /// - [`api_key_id`](BrowserCredentialCreatorBuilder::api_key_id) + /// - [`created_at`](BrowserCredentialCreatorBuilder::created_at) + pub fn build(self) -> Result { + Ok(BrowserCredentialCreator { + kind: self.kind.ok_or_else(|| BuildError::missing_field("kind"))?, + api_key_id: self.api_key_id.ok_or_else(|| BuildError::missing_field("api_key_id"))?, + created_at: self.created_at.ok_or_else(|| BuildError::missing_field("created_at"))?, + }) + } +} diff --git a/agentmail-types/src/types/browser_credential_creator_kind.rs b/agentmail-types/src/types/browser_credential_creator_kind.rs new file mode 100644 index 0000000..f24ba62 --- /dev/null +++ b/agentmail-types/src/types/browser_credential_creator_kind.rs @@ -0,0 +1,17 @@ +pub use crate::prelude::*; +#[allow(unused_imports)] +use super::*; + +#[derive(Debug, Clone, Serialize, Deserialize, PartialEq, Eq, Hash)] +pub enum BrowserCredentialCreatorKind { + #[serde(rename = "bearer_api_key")] + BearerApiKey, +} +impl fmt::Display for BrowserCredentialCreatorKind { + fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result { + let s = match self { + Self::BearerApiKey => "bearer_api_key", + }; + write!(f, "{}", s) + } +} diff --git a/agentmail-types/src/types/browser_enrollment_accepted.rs b/agentmail-types/src/types/browser_enrollment_accepted.rs new file mode 100644 index 0000000..3640f22 --- /dev/null +++ b/agentmail-types/src/types/browser_enrollment_accepted.rs @@ -0,0 +1,60 @@ +pub use crate::prelude::*; +#[allow(unused_imports)] +use super::*; + +/// Pending enrollment receipt. The browser completes key creation and proof +/// on the existing AgentID page. This response contains no URL, token, or +/// navigation instruction. +#[derive(Debug, Clone, Serialize, Deserialize, PartialEq, Eq, Hash)] +pub struct BrowserEnrollmentAccepted { + pub status: BrowserEnrollmentAcceptedStatus, + #[serde(default)] + pub enrollment_id: String, + /// Unix timestamp after which the pending enrollment cannot be activated. + #[serde(default)] + pub expires_at: i64, +} + +impl BrowserEnrollmentAccepted { + pub fn builder() -> BrowserEnrollmentAcceptedBuilder { + ::default() + } +} + +#[derive(Clone, PartialEq, Default, Debug)] +#[non_exhaustive] +pub struct BrowserEnrollmentAcceptedBuilder { + status: Option, + enrollment_id: Option, + expires_at: Option, +} + +impl BrowserEnrollmentAcceptedBuilder { + pub fn status(mut self, value: BrowserEnrollmentAcceptedStatus) -> Self { + self.status = Some(value); + self + } + + pub fn enrollment_id(mut self, value: impl Into) -> Self { + self.enrollment_id = Some(value.into()); + self + } + + pub fn expires_at(mut self, value: i64) -> Self { + self.expires_at = Some(value); + self + } + + /// Consumes the builder and constructs a [`BrowserEnrollmentAccepted`]. + /// This method will fail if any of the following fields are not set: + /// - [`status`](BrowserEnrollmentAcceptedBuilder::status) + /// - [`enrollment_id`](BrowserEnrollmentAcceptedBuilder::enrollment_id) + /// - [`expires_at`](BrowserEnrollmentAcceptedBuilder::expires_at) + pub fn build(self) -> Result { + Ok(BrowserEnrollmentAccepted { + status: self.status.ok_or_else(|| BuildError::missing_field("status"))?, + enrollment_id: self.enrollment_id.ok_or_else(|| BuildError::missing_field("enrollment_id"))?, + expires_at: self.expires_at.ok_or_else(|| BuildError::missing_field("expires_at"))?, + }) + } +} diff --git a/agentmail-types/src/types/browser_enrollment_accepted_status.rs b/agentmail-types/src/types/browser_enrollment_accepted_status.rs new file mode 100644 index 0000000..13ce663 --- /dev/null +++ b/agentmail-types/src/types/browser_enrollment_accepted_status.rs @@ -0,0 +1,17 @@ +pub use crate::prelude::*; +#[allow(unused_imports)] +use super::*; + +#[derive(Debug, Clone, Serialize, Deserialize, PartialEq, Eq, Hash)] +pub enum BrowserEnrollmentAcceptedStatus { + #[serde(rename = "pending")] + Pending, +} +impl fmt::Display for BrowserEnrollmentAcceptedStatus { + fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result { + let s = match self { + Self::Pending => "pending", + }; + write!(f, "{}", s) + } +} diff --git a/agentmail-types/src/types/browser_enrollment_lifecycle_event.rs b/agentmail-types/src/types/browser_enrollment_lifecycle_event.rs new file mode 100644 index 0000000..c88c94d --- /dev/null +++ b/agentmail-types/src/types/browser_enrollment_lifecycle_event.rs @@ -0,0 +1,116 @@ +pub use crate::prelude::*; +#[allow(unused_imports)] +use super::*; + +#[derive(Debug, Clone, Serialize, Deserialize, PartialEq, Eq, Hash)] +pub struct BrowserEnrollmentLifecycleEvent { + pub r#type: BrowserEnrollmentLifecycleEventType, + #[serde(default)] + pub trace_id: String, + #[serde(default)] + pub event_id: String, + #[serde(default)] + #[serde(with = "crate::core::flexible_datetime::offset")] + pub occurred_at: DateTime, + #[serde(default)] + pub organization_id: String, + #[serde(default)] + pub pod_id: String, + pub actor: BrowserLifecycleActor, + #[serde(default)] + pub enrollment_id: String, + #[serde(default)] + pub credential_id: String, +} + +impl BrowserEnrollmentLifecycleEvent { + pub fn builder() -> BrowserEnrollmentLifecycleEventBuilder { + ::default() + } +} + +#[derive(Clone, PartialEq, Default, Debug)] +#[non_exhaustive] +pub struct BrowserEnrollmentLifecycleEventBuilder { + r#type: Option, + trace_id: Option, + event_id: Option, + occurred_at: Option>, + organization_id: Option, + pod_id: Option, + actor: Option, + enrollment_id: Option, + credential_id: Option, +} + +impl BrowserEnrollmentLifecycleEventBuilder { + pub fn r#type(mut self, value: BrowserEnrollmentLifecycleEventType) -> Self { + self.r#type = Some(value); + self + } + + pub fn trace_id(mut self, value: impl Into) -> Self { + self.trace_id = Some(value.into()); + self + } + + pub fn event_id(mut self, value: impl Into) -> Self { + self.event_id = Some(value.into()); + self + } + + pub fn occurred_at(mut self, value: DateTime) -> Self { + self.occurred_at = Some(value); + self + } + + pub fn organization_id(mut self, value: impl Into) -> Self { + self.organization_id = Some(value.into()); + self + } + + pub fn pod_id(mut self, value: impl Into) -> Self { + self.pod_id = Some(value.into()); + self + } + + pub fn actor(mut self, value: BrowserLifecycleActor) -> Self { + self.actor = Some(value); + self + } + + pub fn enrollment_id(mut self, value: impl Into) -> Self { + self.enrollment_id = Some(value.into()); + self + } + + pub fn credential_id(mut self, value: impl Into) -> Self { + self.credential_id = Some(value.into()); + self + } + + /// Consumes the builder and constructs a [`BrowserEnrollmentLifecycleEvent`]. + /// This method will fail if any of the following fields are not set: + /// - [`r#type`](BrowserEnrollmentLifecycleEventBuilder::r#type) + /// - [`trace_id`](BrowserEnrollmentLifecycleEventBuilder::trace_id) + /// - [`event_id`](BrowserEnrollmentLifecycleEventBuilder::event_id) + /// - [`occurred_at`](BrowserEnrollmentLifecycleEventBuilder::occurred_at) + /// - [`organization_id`](BrowserEnrollmentLifecycleEventBuilder::organization_id) + /// - [`pod_id`](BrowserEnrollmentLifecycleEventBuilder::pod_id) + /// - [`actor`](BrowserEnrollmentLifecycleEventBuilder::actor) + /// - [`enrollment_id`](BrowserEnrollmentLifecycleEventBuilder::enrollment_id) + /// - [`credential_id`](BrowserEnrollmentLifecycleEventBuilder::credential_id) + pub fn build(self) -> Result { + Ok(BrowserEnrollmentLifecycleEvent { + r#type: self.r#type.ok_or_else(|| BuildError::missing_field("r#type"))?, + trace_id: self.trace_id.ok_or_else(|| BuildError::missing_field("trace_id"))?, + event_id: self.event_id.ok_or_else(|| BuildError::missing_field("event_id"))?, + occurred_at: self.occurred_at.ok_or_else(|| BuildError::missing_field("occurred_at"))?, + organization_id: self.organization_id.ok_or_else(|| BuildError::missing_field("organization_id"))?, + pod_id: self.pod_id.ok_or_else(|| BuildError::missing_field("pod_id"))?, + actor: self.actor.ok_or_else(|| BuildError::missing_field("actor"))?, + enrollment_id: self.enrollment_id.ok_or_else(|| BuildError::missing_field("enrollment_id"))?, + credential_id: self.credential_id.ok_or_else(|| BuildError::missing_field("credential_id"))?, + }) + } +} diff --git a/agentmail-types/src/types/browser_enrollment_lifecycle_event_type.rs b/agentmail-types/src/types/browser_enrollment_lifecycle_event_type.rs new file mode 100644 index 0000000..223840d --- /dev/null +++ b/agentmail-types/src/types/browser_enrollment_lifecycle_event_type.rs @@ -0,0 +1,52 @@ +pub use crate::prelude::*; +#[allow(unused_imports)] +use super::*; + +#[non_exhaustive] +#[derive(Debug, Clone, PartialEq, Eq, Hash)] +pub enum BrowserEnrollmentLifecycleEventType { + BrowserEnrollmentIntentCreated, + BrowserCredentialActivated, + BrowserEnrollmentCancelled, + BrowserCredentialDeleted, + /// This variant is used for forward compatibility. + /// If the server sends a value not recognized by the current SDK version, + /// it will be captured here with the raw string value. + __Unknown(String), +} +impl Serialize for BrowserEnrollmentLifecycleEventType { + fn serialize(&self, serializer: S) -> Result { + match self { + Self::BrowserEnrollmentIntentCreated => serializer.serialize_str("browser_enrollment_intent_created"), + Self::BrowserCredentialActivated => serializer.serialize_str("browser_credential_activated"), + Self::BrowserEnrollmentCancelled => serializer.serialize_str("browser_enrollment_cancelled"), + Self::BrowserCredentialDeleted => serializer.serialize_str("browser_credential_deleted"), + Self::__Unknown(val) => serializer.serialize_str(val), + } + } +} + +impl<'de> Deserialize<'de> for BrowserEnrollmentLifecycleEventType { + fn deserialize>(deserializer: D) -> Result { + let value = String::deserialize(deserializer)?; + match value.as_str() { + "browser_enrollment_intent_created" => Ok(Self::BrowserEnrollmentIntentCreated), + "browser_credential_activated" => Ok(Self::BrowserCredentialActivated), + "browser_enrollment_cancelled" => Ok(Self::BrowserEnrollmentCancelled), + "browser_credential_deleted" => Ok(Self::BrowserCredentialDeleted), + _ => Ok(Self::__Unknown(value)), + } + } +} + +impl fmt::Display for BrowserEnrollmentLifecycleEventType { + fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result { + match self { + Self::BrowserEnrollmentIntentCreated => write!(f, "browser_enrollment_intent_created"), + Self::BrowserCredentialActivated => write!(f, "browser_credential_activated"), + Self::BrowserEnrollmentCancelled => write!(f, "browser_enrollment_cancelled"), + Self::BrowserCredentialDeleted => write!(f, "browser_credential_deleted"), + Self::__Unknown(val) => write!(f, "{}", val), + } + } +} diff --git a/agentmail-types/src/types/browser_enrollment_transaction_jti.rs b/agentmail-types/src/types/browser_enrollment_transaction_jti.rs new file mode 100644 index 0000000..b15a314 --- /dev/null +++ b/agentmail-types/src/types/browser_enrollment_transaction_jti.rs @@ -0,0 +1,6 @@ +pub use crate::prelude::*; +#[allow(unused_imports)] +use super::*; + +#[derive(Debug, Clone, Serialize, Deserialize, PartialEq, Eq, Hash, Default)] +pub struct BrowserEnrollmentTransactionJti(pub String); \ No newline at end of file diff --git a/agentmail-types/src/types/browser_lifecycle_actor.rs b/agentmail-types/src/types/browser_lifecycle_actor.rs new file mode 100644 index 0000000..75d9667 --- /dev/null +++ b/agentmail-types/src/types/browser_lifecycle_actor.rs @@ -0,0 +1,59 @@ +pub use crate::prelude::*; +#[allow(unused_imports)] +use super::*; + +#[derive(Debug, Clone, Serialize, Deserialize, PartialEq, Eq, Hash)] +#[serde(untagged)] +pub enum BrowserLifecycleActor { + BrowserLifecycleActorZero(BrowserLifecycleActorZero), + + BrowserLifecycleActorOne(BrowserLifecycleActorOne), +} + +impl BrowserLifecycleActor { + pub fn is_browser_lifecycle_actor_zero(&self) -> bool { + matches!(self, Self::BrowserLifecycleActorZero(_)) + } + + pub fn is_browser_lifecycle_actor_one(&self) -> bool { + matches!(self, Self::BrowserLifecycleActorOne(_)) + } + + + pub fn as_browser_lifecycle_actor_zero(&self) -> Option<&BrowserLifecycleActorZero> { + match self { + Self::BrowserLifecycleActorZero(value) => Some(value), + _ => None, + } + } + + pub fn into_browser_lifecycle_actor_zero(self) -> Option { + match self { + Self::BrowserLifecycleActorZero(value) => Some(value), + _ => None, + } + } + + pub fn as_browser_lifecycle_actor_one(&self) -> Option<&BrowserLifecycleActorOne> { + match self { + Self::BrowserLifecycleActorOne(value) => Some(value), + _ => None, + } + } + + pub fn into_browser_lifecycle_actor_one(self) -> Option { + match self { + Self::BrowserLifecycleActorOne(value) => Some(value), + _ => None, + } + } +} + +impl fmt::Display for BrowserLifecycleActor { + fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result { + match self { + Self::BrowserLifecycleActorZero(value) => write!(f, "{}", serde_json::to_string(value).unwrap_or_else(|_| format!("{:?}", value))), + Self::BrowserLifecycleActorOne(value) => write!(f, "{}", serde_json::to_string(value).unwrap_or_else(|_| format!("{:?}", value))), + } + } +} diff --git a/agentmail-types/src/types/browser_lifecycle_actor_one.rs b/agentmail-types/src/types/browser_lifecycle_actor_one.rs new file mode 100644 index 0000000..facdead --- /dev/null +++ b/agentmail-types/src/types/browser_lifecycle_actor_one.rs @@ -0,0 +1,46 @@ +pub use crate::prelude::*; +#[allow(unused_imports)] +use super::*; + +#[derive(Debug, Clone, Serialize, Deserialize, PartialEq, Eq, Hash)] +pub struct BrowserLifecycleActorOne { + #[serde(flatten)] + pub browser_lifecycle_credential_actor_fields: BrowserLifecycleCredentialActor, + pub r#type: BrowserLifecycleActorOneType, +} + +impl BrowserLifecycleActorOne { + pub fn builder() -> BrowserLifecycleActorOneBuilder { + ::default() + } +} + +#[derive(Clone, PartialEq, Default, Debug)] +#[non_exhaustive] +pub struct BrowserLifecycleActorOneBuilder { + browser_lifecycle_credential_actor_fields: Option, + r#type: Option, +} + +impl BrowserLifecycleActorOneBuilder { + pub fn browser_lifecycle_credential_actor_fields(mut self, value: BrowserLifecycleCredentialActor) -> Self { + self.browser_lifecycle_credential_actor_fields = Some(value); + self + } + + pub fn r#type(mut self, value: BrowserLifecycleActorOneType) -> Self { + self.r#type = Some(value); + self + } + + /// Consumes the builder and constructs a [`BrowserLifecycleActorOne`]. + /// This method will fail if any of the following fields are not set: + /// - [`browser_lifecycle_credential_actor_fields`](BrowserLifecycleActorOneBuilder::browser_lifecycle_credential_actor_fields) + /// - [`r#type`](BrowserLifecycleActorOneBuilder::r#type) + pub fn build(self) -> Result { + Ok(BrowserLifecycleActorOne { + browser_lifecycle_credential_actor_fields: self.browser_lifecycle_credential_actor_fields.ok_or_else(|| BuildError::missing_field("browser_lifecycle_credential_actor_fields"))?, + r#type: self.r#type.ok_or_else(|| BuildError::missing_field("r#type"))?, + }) + } +} diff --git a/agentmail-types/src/types/browser_lifecycle_actor_one_type.rs b/agentmail-types/src/types/browser_lifecycle_actor_one_type.rs new file mode 100644 index 0000000..ea40bed --- /dev/null +++ b/agentmail-types/src/types/browser_lifecycle_actor_one_type.rs @@ -0,0 +1,17 @@ +pub use crate::prelude::*; +#[allow(unused_imports)] +use super::*; + +#[derive(Debug, Clone, Serialize, Deserialize, PartialEq, Eq, Hash)] +pub enum BrowserLifecycleActorOneType { + #[serde(rename = "browser_credential")] + BrowserCredential, +} +impl fmt::Display for BrowserLifecycleActorOneType { + fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result { + let s = match self { + Self::BrowserCredential => "browser_credential", + }; + write!(f, "{}", s) + } +} diff --git a/agentmail-types/src/types/browser_lifecycle_actor_zero.rs b/agentmail-types/src/types/browser_lifecycle_actor_zero.rs new file mode 100644 index 0000000..2db4012 --- /dev/null +++ b/agentmail-types/src/types/browser_lifecycle_actor_zero.rs @@ -0,0 +1,46 @@ +pub use crate::prelude::*; +#[allow(unused_imports)] +use super::*; + +#[derive(Debug, Clone, Serialize, Deserialize, PartialEq, Eq, Hash)] +pub struct BrowserLifecycleActorZero { + #[serde(flatten)] + pub browser_lifecycle_api_key_actor_fields: BrowserLifecycleApiKeyActor, + pub r#type: BrowserLifecycleActorZeroType, +} + +impl BrowserLifecycleActorZero { + pub fn builder() -> BrowserLifecycleActorZeroBuilder { + ::default() + } +} + +#[derive(Clone, PartialEq, Default, Debug)] +#[non_exhaustive] +pub struct BrowserLifecycleActorZeroBuilder { + browser_lifecycle_api_key_actor_fields: Option, + r#type: Option, +} + +impl BrowserLifecycleActorZeroBuilder { + pub fn browser_lifecycle_api_key_actor_fields(mut self, value: BrowserLifecycleApiKeyActor) -> Self { + self.browser_lifecycle_api_key_actor_fields = Some(value); + self + } + + pub fn r#type(mut self, value: BrowserLifecycleActorZeroType) -> Self { + self.r#type = Some(value); + self + } + + /// Consumes the builder and constructs a [`BrowserLifecycleActorZero`]. + /// This method will fail if any of the following fields are not set: + /// - [`browser_lifecycle_api_key_actor_fields`](BrowserLifecycleActorZeroBuilder::browser_lifecycle_api_key_actor_fields) + /// - [`r#type`](BrowserLifecycleActorZeroBuilder::r#type) + pub fn build(self) -> Result { + Ok(BrowserLifecycleActorZero { + browser_lifecycle_api_key_actor_fields: self.browser_lifecycle_api_key_actor_fields.ok_or_else(|| BuildError::missing_field("browser_lifecycle_api_key_actor_fields"))?, + r#type: self.r#type.ok_or_else(|| BuildError::missing_field("r#type"))?, + }) + } +} diff --git a/agentmail-types/src/types/browser_lifecycle_actor_zero_type.rs b/agentmail-types/src/types/browser_lifecycle_actor_zero_type.rs new file mode 100644 index 0000000..5544c66 --- /dev/null +++ b/agentmail-types/src/types/browser_lifecycle_actor_zero_type.rs @@ -0,0 +1,17 @@ +pub use crate::prelude::*; +#[allow(unused_imports)] +use super::*; + +#[derive(Debug, Clone, Serialize, Deserialize, PartialEq, Eq, Hash)] +pub enum BrowserLifecycleActorZeroType { + #[serde(rename = "api_key")] + ApiKey, +} +impl fmt::Display for BrowserLifecycleActorZeroType { + fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result { + let s = match self { + Self::ApiKey => "api_key", + }; + write!(f, "{}", s) + } +} diff --git a/agentmail-types/src/types/browser_lifecycle_api_key_actor.rs b/agentmail-types/src/types/browser_lifecycle_api_key_actor.rs new file mode 100644 index 0000000..64bc33d --- /dev/null +++ b/agentmail-types/src/types/browser_lifecycle_api_key_actor.rs @@ -0,0 +1,37 @@ +pub use crate::prelude::*; +#[allow(unused_imports)] +use super::*; + +#[derive(Debug, Clone, Serialize, Deserialize, Default, PartialEq, Eq, Hash)] +pub struct BrowserLifecycleApiKeyActor { + #[serde(default)] + pub api_key_id: String, +} + +impl BrowserLifecycleApiKeyActor { + pub fn builder() -> BrowserLifecycleApiKeyActorBuilder { + ::default() + } +} + +#[derive(Clone, PartialEq, Default, Debug)] +#[non_exhaustive] +pub struct BrowserLifecycleApiKeyActorBuilder { + api_key_id: Option, +} + +impl BrowserLifecycleApiKeyActorBuilder { + pub fn api_key_id(mut self, value: impl Into) -> Self { + self.api_key_id = Some(value.into()); + self + } + + /// Consumes the builder and constructs a [`BrowserLifecycleApiKeyActor`]. + /// This method will fail if any of the following fields are not set: + /// - [`api_key_id`](BrowserLifecycleApiKeyActorBuilder::api_key_id) + pub fn build(self) -> Result { + Ok(BrowserLifecycleApiKeyActor { + api_key_id: self.api_key_id.ok_or_else(|| BuildError::missing_field("api_key_id"))?, + }) + } +} diff --git a/agentmail-types/src/types/browser_lifecycle_credential_actor.rs b/agentmail-types/src/types/browser_lifecycle_credential_actor.rs new file mode 100644 index 0000000..4206dda --- /dev/null +++ b/agentmail-types/src/types/browser_lifecycle_credential_actor.rs @@ -0,0 +1,47 @@ +pub use crate::prelude::*; +#[allow(unused_imports)] +use super::*; + +#[derive(Debug, Clone, Serialize, Deserialize, Default, PartialEq, Eq, Hash)] +pub struct BrowserLifecycleCredentialActor { + #[serde(default)] + pub credential_id: String, + #[serde(default)] + pub authorizing_api_key_id: String, +} + +impl BrowserLifecycleCredentialActor { + pub fn builder() -> BrowserLifecycleCredentialActorBuilder { + ::default() + } +} + +#[derive(Clone, PartialEq, Default, Debug)] +#[non_exhaustive] +pub struct BrowserLifecycleCredentialActorBuilder { + credential_id: Option, + authorizing_api_key_id: Option, +} + +impl BrowserLifecycleCredentialActorBuilder { + pub fn credential_id(mut self, value: impl Into) -> Self { + self.credential_id = Some(value.into()); + self + } + + pub fn authorizing_api_key_id(mut self, value: impl Into) -> Self { + self.authorizing_api_key_id = Some(value.into()); + self + } + + /// Consumes the builder and constructs a [`BrowserLifecycleCredentialActor`]. + /// This method will fail if any of the following fields are not set: + /// - [`credential_id`](BrowserLifecycleCredentialActorBuilder::credential_id) + /// - [`authorizing_api_key_id`](BrowserLifecycleCredentialActorBuilder::authorizing_api_key_id) + pub fn build(self) -> Result { + Ok(BrowserLifecycleCredentialActor { + credential_id: self.credential_id.ok_or_else(|| BuildError::missing_field("credential_id"))?, + authorizing_api_key_id: self.authorizing_api_key_id.ok_or_else(|| BuildError::missing_field("authorizing_api_key_id"))?, + }) + } +} diff --git a/agentmail-types/src/types/browser_lifecycle_event.rs b/agentmail-types/src/types/browser_lifecycle_event.rs new file mode 100644 index 0000000..84ec85f --- /dev/null +++ b/agentmail-types/src/types/browser_lifecycle_event.rs @@ -0,0 +1,59 @@ +pub use crate::prelude::*; +#[allow(unused_imports)] +use super::*; + +#[derive(Debug, Clone, Serialize, Deserialize, PartialEq, Eq, Hash)] +#[serde(untagged)] +pub enum BrowserLifecycleEvent { + BrowserEnrollmentLifecycleEvent(BrowserEnrollmentLifecycleEvent), + + BrowserConsentLifecycleEvent(BrowserConsentLifecycleEvent), +} + +impl BrowserLifecycleEvent { + pub fn is_browser_enrollment_lifecycle_event(&self) -> bool { + matches!(self, Self::BrowserEnrollmentLifecycleEvent(_)) + } + + pub fn is_browser_consent_lifecycle_event(&self) -> bool { + matches!(self, Self::BrowserConsentLifecycleEvent(_)) + } + + + pub fn as_browser_enrollment_lifecycle_event(&self) -> Option<&BrowserEnrollmentLifecycleEvent> { + match self { + Self::BrowserEnrollmentLifecycleEvent(value) => Some(value), + _ => None, + } + } + + pub fn into_browser_enrollment_lifecycle_event(self) -> Option { + match self { + Self::BrowserEnrollmentLifecycleEvent(value) => Some(value), + _ => None, + } + } + + pub fn as_browser_consent_lifecycle_event(&self) -> Option<&BrowserConsentLifecycleEvent> { + match self { + Self::BrowserConsentLifecycleEvent(value) => Some(value), + _ => None, + } + } + + pub fn into_browser_consent_lifecycle_event(self) -> Option { + match self { + Self::BrowserConsentLifecycleEvent(value) => Some(value), + _ => None, + } + } +} + +impl fmt::Display for BrowserLifecycleEvent { + fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result { + match self { + Self::BrowserEnrollmentLifecycleEvent(value) => write!(f, "{}", serde_json::to_string(value).unwrap_or_else(|_| format!("{:?}", value))), + Self::BrowserConsentLifecycleEvent(value) => write!(f, "{}", serde_json::to_string(value).unwrap_or_else(|_| format!("{:?}", value))), + } + } +} diff --git a/agentmail-types/src/types/create_browser_enrollment_request.rs b/agentmail-types/src/types/create_browser_enrollment_request.rs new file mode 100644 index 0000000..026cfb3 --- /dev/null +++ b/agentmail-types/src/types/create_browser_enrollment_request.rs @@ -0,0 +1,38 @@ +pub use crate::prelude::*; +#[allow(unused_imports)] +use super::*; + +#[derive(Debug, Clone, Serialize, Deserialize, Default, PartialEq, Eq, Hash)] +pub struct CreateBrowserEnrollmentRequest { + #[serde(default)] + pub transaction_jti: BrowserEnrollmentTransactionJti, +} + +impl CreateBrowserEnrollmentRequest { + pub fn builder() -> CreateBrowserEnrollmentRequestBuilder { + ::default() + } +} + +#[derive(Clone, PartialEq, Default, Debug)] +#[non_exhaustive] +pub struct CreateBrowserEnrollmentRequestBuilder { + transaction_jti: Option, +} + +impl CreateBrowserEnrollmentRequestBuilder { + pub fn transaction_jti(mut self, value: BrowserEnrollmentTransactionJti) -> Self { + self.transaction_jti = Some(value); + self + } + + /// Consumes the builder and constructs a [`CreateBrowserEnrollmentRequest`]. + /// This method will fail if any of the following fields are not set: + /// - [`transaction_jti`](CreateBrowserEnrollmentRequestBuilder::transaction_jti) + pub fn build(self) -> Result { + Ok(CreateBrowserEnrollmentRequest { + transaction_jti: self.transaction_jti.ok_or_else(|| BuildError::missing_field("transaction_jti"))?, + }) + } +} + diff --git a/agentmail-types/src/types/list_browser_consents_query_request.rs b/agentmail-types/src/types/list_browser_consents_query_request.rs new file mode 100644 index 0000000..77a9737 --- /dev/null +++ b/agentmail-types/src/types/list_browser_consents_query_request.rs @@ -0,0 +1,57 @@ +pub use crate::prelude::*; +#[allow(unused_imports)] +use super::*; + +/// Query parameters for list-browser-consents +#[derive(Debug, Clone, Serialize, Deserialize, Default, PartialEq, Eq, Hash)] +pub struct ListBrowserConsentsQueryRequest { + #[serde(default)] + pub inbox_id: String, + #[serde(skip_serializing_if = "Option::is_none")] + pub limit: Option, + #[serde(skip_serializing_if = "Option::is_none")] + pub page_token: Option, +} + +impl ListBrowserConsentsQueryRequest { + pub fn builder() -> ListBrowserConsentsQueryRequestBuilder { + ::default() + } +} + +#[derive(Clone, PartialEq, Default, Debug)] +#[non_exhaustive] +pub struct ListBrowserConsentsQueryRequestBuilder { + inbox_id: Option, + limit: Option, + page_token: Option, +} + +impl ListBrowserConsentsQueryRequestBuilder { + pub fn inbox_id(mut self, value: impl Into) -> Self { + self.inbox_id = Some(value.into()); + self + } + + pub fn limit(mut self, value: BrowserAuthorizationListLimit) -> Self { + self.limit = Some(value); + self + } + + pub fn page_token(mut self, value: PageToken) -> Self { + self.page_token = Some(value); + self + } + + /// Consumes the builder and constructs a [`ListBrowserConsentsQueryRequest`]. + /// This method will fail if any of the following fields are not set: + /// - [`inbox_id`](ListBrowserConsentsQueryRequestBuilder::inbox_id) + pub fn build(self) -> Result { + Ok(ListBrowserConsentsQueryRequest { + inbox_id: self.inbox_id.ok_or_else(|| BuildError::missing_field("inbox_id"))?, + limit: self.limit, + page_token: self.page_token, + }) + } +} + diff --git a/agentmail-types/src/types/list_browser_consents_response.rs b/agentmail-types/src/types/list_browser_consents_response.rs new file mode 100644 index 0000000..2d88a87 --- /dev/null +++ b/agentmail-types/src/types/list_browser_consents_response.rs @@ -0,0 +1,66 @@ +pub use crate::prelude::*; +#[allow(unused_imports)] +use super::*; + +#[derive(Debug, Clone, Serialize, Deserialize, Default, PartialEq, Eq, Hash)] +pub struct ListBrowserConsentsResponse { + #[serde(default)] + pub count: Count, + #[serde(default)] + pub limit: BrowserAuthorizationListLimit, + #[serde(skip_serializing_if = "Option::is_none")] + pub next_page_token: Option, + #[serde(default)] + pub consents: Vec, +} + +impl ListBrowserConsentsResponse { + pub fn builder() -> ListBrowserConsentsResponseBuilder { + ::default() + } +} + +#[derive(Clone, PartialEq, Default, Debug)] +#[non_exhaustive] +pub struct ListBrowserConsentsResponseBuilder { + count: Option, + limit: Option, + next_page_token: Option, + consents: Option>, +} + +impl ListBrowserConsentsResponseBuilder { + pub fn count(mut self, value: Count) -> Self { + self.count = Some(value); + self + } + + pub fn limit(mut self, value: BrowserAuthorizationListLimit) -> Self { + self.limit = Some(value); + self + } + + pub fn next_page_token(mut self, value: PageToken) -> Self { + self.next_page_token = Some(value); + self + } + + pub fn consents(mut self, value: Vec) -> Self { + self.consents = Some(value); + self + } + + /// Consumes the builder and constructs a [`ListBrowserConsentsResponse`]. + /// This method will fail if any of the following fields are not set: + /// - [`count`](ListBrowserConsentsResponseBuilder::count) + /// - [`limit`](ListBrowserConsentsResponseBuilder::limit) + /// - [`consents`](ListBrowserConsentsResponseBuilder::consents) + pub fn build(self) -> Result { + Ok(ListBrowserConsentsResponse { + count: self.count.ok_or_else(|| BuildError::missing_field("count"))?, + limit: self.limit.ok_or_else(|| BuildError::missing_field("limit"))?, + next_page_token: self.next_page_token, + consents: self.consents.ok_or_else(|| BuildError::missing_field("consents"))?, + }) + } +} diff --git a/agentmail-types/src/types/list_browser_credential_events_query_request.rs b/agentmail-types/src/types/list_browser_credential_events_query_request.rs new file mode 100644 index 0000000..6a2da05 --- /dev/null +++ b/agentmail-types/src/types/list_browser_credential_events_query_request.rs @@ -0,0 +1,46 @@ +pub use crate::prelude::*; +#[allow(unused_imports)] +use super::*; + +/// Query parameters for list-browser-credential-events +#[derive(Debug, Clone, Serialize, Deserialize, Default, PartialEq, Eq, Hash)] +pub struct ListBrowserCredentialEventsQueryRequest { + #[serde(skip_serializing_if = "Option::is_none")] + pub limit: Option, + #[serde(skip_serializing_if = "Option::is_none")] + pub page_token: Option, +} + +impl ListBrowserCredentialEventsQueryRequest { + pub fn builder() -> ListBrowserCredentialEventsQueryRequestBuilder { + ::default() + } +} + +#[derive(Clone, PartialEq, Default, Debug)] +#[non_exhaustive] +pub struct ListBrowserCredentialEventsQueryRequestBuilder { + limit: Option, + page_token: Option, +} + +impl ListBrowserCredentialEventsQueryRequestBuilder { + pub fn limit(mut self, value: BrowserAuthorizationListLimit) -> Self { + self.limit = Some(value); + self + } + + pub fn page_token(mut self, value: PageToken) -> Self { + self.page_token = Some(value); + self + } + + /// Consumes the builder and constructs a [`ListBrowserCredentialEventsQueryRequest`]. + pub fn build(self) -> Result { + Ok(ListBrowserCredentialEventsQueryRequest { + limit: self.limit, + page_token: self.page_token, + }) + } +} + diff --git a/agentmail-types/src/types/list_browser_credentials_query_request.rs b/agentmail-types/src/types/list_browser_credentials_query_request.rs new file mode 100644 index 0000000..814498d --- /dev/null +++ b/agentmail-types/src/types/list_browser_credentials_query_request.rs @@ -0,0 +1,46 @@ +pub use crate::prelude::*; +#[allow(unused_imports)] +use super::*; + +/// Query parameters for list-browser-credentials +#[derive(Debug, Clone, Serialize, Deserialize, Default, PartialEq, Eq, Hash)] +pub struct ListBrowserCredentialsQueryRequest { + #[serde(skip_serializing_if = "Option::is_none")] + pub limit: Option, + #[serde(skip_serializing_if = "Option::is_none")] + pub page_token: Option, +} + +impl ListBrowserCredentialsQueryRequest { + pub fn builder() -> ListBrowserCredentialsQueryRequestBuilder { + ::default() + } +} + +#[derive(Clone, PartialEq, Default, Debug)] +#[non_exhaustive] +pub struct ListBrowserCredentialsQueryRequestBuilder { + limit: Option, + page_token: Option, +} + +impl ListBrowserCredentialsQueryRequestBuilder { + pub fn limit(mut self, value: BrowserAuthorizationListLimit) -> Self { + self.limit = Some(value); + self + } + + pub fn page_token(mut self, value: PageToken) -> Self { + self.page_token = Some(value); + self + } + + /// Consumes the builder and constructs a [`ListBrowserCredentialsQueryRequest`]. + pub fn build(self) -> Result { + Ok(ListBrowserCredentialsQueryRequest { + limit: self.limit, + page_token: self.page_token, + }) + } +} + diff --git a/agentmail-types/src/types/list_browser_credentials_response.rs b/agentmail-types/src/types/list_browser_credentials_response.rs new file mode 100644 index 0000000..b02e3f6 --- /dev/null +++ b/agentmail-types/src/types/list_browser_credentials_response.rs @@ -0,0 +1,66 @@ +pub use crate::prelude::*; +#[allow(unused_imports)] +use super::*; + +#[derive(Debug, Clone, Serialize, Deserialize, Default, PartialEq, Eq, Hash)] +pub struct ListBrowserCredentialsResponse { + #[serde(default)] + pub count: Count, + #[serde(default)] + pub limit: BrowserAuthorizationListLimit, + #[serde(skip_serializing_if = "Option::is_none")] + pub next_page_token: Option, + #[serde(default)] + pub credentials: Vec, +} + +impl ListBrowserCredentialsResponse { + pub fn builder() -> ListBrowserCredentialsResponseBuilder { + ::default() + } +} + +#[derive(Clone, PartialEq, Default, Debug)] +#[non_exhaustive] +pub struct ListBrowserCredentialsResponseBuilder { + count: Option, + limit: Option, + next_page_token: Option, + credentials: Option>, +} + +impl ListBrowserCredentialsResponseBuilder { + pub fn count(mut self, value: Count) -> Self { + self.count = Some(value); + self + } + + pub fn limit(mut self, value: BrowserAuthorizationListLimit) -> Self { + self.limit = Some(value); + self + } + + pub fn next_page_token(mut self, value: PageToken) -> Self { + self.next_page_token = Some(value); + self + } + + pub fn credentials(mut self, value: Vec) -> Self { + self.credentials = Some(value); + self + } + + /// Consumes the builder and constructs a [`ListBrowserCredentialsResponse`]. + /// This method will fail if any of the following fields are not set: + /// - [`count`](ListBrowserCredentialsResponseBuilder::count) + /// - [`limit`](ListBrowserCredentialsResponseBuilder::limit) + /// - [`credentials`](ListBrowserCredentialsResponseBuilder::credentials) + pub fn build(self) -> Result { + Ok(ListBrowserCredentialsResponse { + count: self.count.ok_or_else(|| BuildError::missing_field("count"))?, + limit: self.limit.ok_or_else(|| BuildError::missing_field("limit"))?, + next_page_token: self.next_page_token, + credentials: self.credentials.ok_or_else(|| BuildError::missing_field("credentials"))?, + }) + } +} diff --git a/agentmail-types/src/types/list_browser_lifecycle_events_response.rs b/agentmail-types/src/types/list_browser_lifecycle_events_response.rs new file mode 100644 index 0000000..0fdce23 --- /dev/null +++ b/agentmail-types/src/types/list_browser_lifecycle_events_response.rs @@ -0,0 +1,66 @@ +pub use crate::prelude::*; +#[allow(unused_imports)] +use super::*; + +#[derive(Debug, Clone, Serialize, Deserialize, Default, PartialEq, Eq, Hash)] +pub struct ListBrowserLifecycleEventsResponse { + #[serde(default)] + pub count: Count, + #[serde(default)] + pub limit: BrowserAuthorizationListLimit, + #[serde(skip_serializing_if = "Option::is_none")] + pub next_page_token: Option, + #[serde(default)] + pub events: Vec, +} + +impl ListBrowserLifecycleEventsResponse { + pub fn builder() -> ListBrowserLifecycleEventsResponseBuilder { + ::default() + } +} + +#[derive(Clone, PartialEq, Default, Debug)] +#[non_exhaustive] +pub struct ListBrowserLifecycleEventsResponseBuilder { + count: Option, + limit: Option, + next_page_token: Option, + events: Option>, +} + +impl ListBrowserLifecycleEventsResponseBuilder { + pub fn count(mut self, value: Count) -> Self { + self.count = Some(value); + self + } + + pub fn limit(mut self, value: BrowserAuthorizationListLimit) -> Self { + self.limit = Some(value); + self + } + + pub fn next_page_token(mut self, value: PageToken) -> Self { + self.next_page_token = Some(value); + self + } + + pub fn events(mut self, value: Vec) -> Self { + self.events = Some(value); + self + } + + /// Consumes the builder and constructs a [`ListBrowserLifecycleEventsResponse`]. + /// This method will fail if any of the following fields are not set: + /// - [`count`](ListBrowserLifecycleEventsResponseBuilder::count) + /// - [`limit`](ListBrowserLifecycleEventsResponseBuilder::limit) + /// - [`events`](ListBrowserLifecycleEventsResponseBuilder::events) + pub fn build(self) -> Result { + Ok(ListBrowserLifecycleEventsResponse { + count: self.count.ok_or_else(|| BuildError::missing_field("count"))?, + limit: self.limit.ok_or_else(|| BuildError::missing_field("limit"))?, + next_page_token: self.next_page_token, + events: self.events.ok_or_else(|| BuildError::missing_field("events"))?, + }) + } +} diff --git a/agentmail-types/src/types/mod.rs b/agentmail-types/src/types/mod.rs index 3a53a6b..683a899 100644 --- a/agentmail-types/src/types/mod.rs +++ b/agentmail-types/src/types/mod.rs @@ -5,8 +5,8 @@ //! //! ## Type Categories //! -//! - **Request/Response Types**: 90 types for API operations -//! - **Model Types**: 223 types for data representation +//! - **Request/Response Types**: 97 types for API operations +//! - **Model Types**: 245 types for data representation pub mod limit; pub mod count; @@ -83,6 +83,31 @@ pub mod public_key_credential_type; pub mod public_key_credential; pub mod list_public_keys_response; pub mod revoke_all_agent_id_sign_in_keys_response; +pub mod browser_enrollment_transaction_jti; +pub mod browser_enrollment_accepted_status; +pub mod browser_enrollment_accepted; +pub mod browser_credential_creator_kind; +pub mod browser_credential_creator; +pub mod browser_credential; +pub mod browser_consent_client_type; +pub mod browser_consent; +pub mod browser_lifecycle_api_key_actor; +pub mod browser_lifecycle_credential_actor; +pub mod browser_lifecycle_actor_zero_type; +pub mod browser_lifecycle_actor_zero; +pub mod browser_lifecycle_actor_one_type; +pub mod browser_lifecycle_actor_one; +pub mod browser_lifecycle_actor; +pub mod browser_enrollment_lifecycle_event_type; +pub mod browser_consent_lifecycle_event_type; +pub mod browser_enrollment_lifecycle_event; +pub mod browser_consent_lifecycle_event_client_type; +pub mod browser_consent_lifecycle_event; +pub mod browser_lifecycle_event; +pub mod browser_authorization_list_limit; +pub mod list_browser_credentials_response; +pub mod list_browser_consents_response; +pub mod list_browser_lifecycle_events_response; pub mod api_key_permissions; pub mod api_key; pub mod create_api_key_response; @@ -282,6 +307,7 @@ pub mod agent_signup_request; pub mod agent_verify_request; pub mod create_public_key_request; pub mod update_public_key_name_request; +pub mod create_browser_enrollment_request; pub mod create_draft_request; pub mod update_draft_request; pub mod batch_get_messages_request; @@ -293,6 +319,9 @@ pub mod pods_list_query_request; pub mod webhooks_list_query_request; pub mod api_keys_list_query_request; pub mod list_public_keys_query_request; +pub mod list_browser_credentials_query_request; +pub mod list_browser_credential_events_query_request; +pub mod list_browser_consents_query_request; pub mod domains_list_query_request; pub mod drafts_list_query_request; pub mod lists_list_query_request; @@ -397,6 +426,31 @@ pub use public_key_credential_type::PublicKeyCredentialType; pub use public_key_credential::PublicKeyCredential; pub use list_public_keys_response::ListPublicKeysResponse; pub use revoke_all_agent_id_sign_in_keys_response::RevokeAllAgentIdSignInKeysResponse; +pub use browser_enrollment_transaction_jti::BrowserEnrollmentTransactionJti; +pub use browser_enrollment_accepted_status::BrowserEnrollmentAcceptedStatus; +pub use browser_enrollment_accepted::BrowserEnrollmentAccepted; +pub use browser_credential_creator_kind::BrowserCredentialCreatorKind; +pub use browser_credential_creator::BrowserCredentialCreator; +pub use browser_credential::BrowserCredential; +pub use browser_consent_client_type::BrowserConsentClientType; +pub use browser_consent::BrowserConsent; +pub use browser_lifecycle_api_key_actor::BrowserLifecycleApiKeyActor; +pub use browser_lifecycle_credential_actor::BrowserLifecycleCredentialActor; +pub use browser_lifecycle_actor_zero_type::BrowserLifecycleActorZeroType; +pub use browser_lifecycle_actor_zero::BrowserLifecycleActorZero; +pub use browser_lifecycle_actor_one_type::BrowserLifecycleActorOneType; +pub use browser_lifecycle_actor_one::BrowserLifecycleActorOne; +pub use browser_lifecycle_actor::BrowserLifecycleActor; +pub use browser_enrollment_lifecycle_event_type::BrowserEnrollmentLifecycleEventType; +pub use browser_consent_lifecycle_event_type::BrowserConsentLifecycleEventType; +pub use browser_enrollment_lifecycle_event::BrowserEnrollmentLifecycleEvent; +pub use browser_consent_lifecycle_event_client_type::BrowserConsentLifecycleEventClientType; +pub use browser_consent_lifecycle_event::BrowserConsentLifecycleEvent; +pub use browser_lifecycle_event::BrowserLifecycleEvent; +pub use browser_authorization_list_limit::BrowserAuthorizationListLimit; +pub use list_browser_credentials_response::ListBrowserCredentialsResponse; +pub use list_browser_consents_response::ListBrowserConsentsResponse; +pub use list_browser_lifecycle_events_response::ListBrowserLifecycleEventsResponse; pub use api_key_permissions::ApiKeyPermissions; pub use api_key::ApiKey; pub use create_api_key_response::CreateApiKeyResponse; @@ -596,6 +650,7 @@ pub use agent_signup_request::AgentSignupRequest; pub use agent_verify_request::AgentVerifyRequest; pub use create_public_key_request::CreatePublicKeyRequest; pub use update_public_key_name_request::UpdatePublicKeyNameRequest; +pub use create_browser_enrollment_request::CreateBrowserEnrollmentRequest; pub use create_draft_request::CreateDraftRequest; pub use update_draft_request::UpdateDraftRequest; pub use batch_get_messages_request::BatchGetMessagesRequest; @@ -607,6 +662,9 @@ pub use pods_list_query_request::PodsListQueryRequest; pub use webhooks_list_query_request::WebhooksListQueryRequest; pub use api_keys_list_query_request::ApiKeysListQueryRequest; pub use list_public_keys_query_request::ListPublicKeysQueryRequest; +pub use list_browser_credentials_query_request::ListBrowserCredentialsQueryRequest; +pub use list_browser_credential_events_query_request::ListBrowserCredentialEventsQueryRequest; +pub use list_browser_consents_query_request::ListBrowserConsentsQueryRequest; pub use domains_list_query_request::DomainsListQueryRequest; pub use drafts_list_query_request::DraftsListQueryRequest; pub use lists_list_query_request::ListsListQueryRequest; diff --git a/agentmail-types/src/types/send_attachment.rs b/agentmail-types/src/types/send_attachment.rs index 588c286..7f3a4eb 100644 --- a/agentmail-types/src/types/send_attachment.rs +++ b/agentmail-types/src/types/send_attachment.rs @@ -2,6 +2,7 @@ pub use crate::prelude::*; #[allow(unused_imports)] use super::*; +/// Provide either `content` or `url` for each attachment. #[derive(Debug, Clone, Serialize, Deserialize, Default, PartialEq, Eq, Hash)] pub struct SendAttachment { #[serde(skip_serializing_if = "Option::is_none")] @@ -12,10 +13,12 @@ pub struct SendAttachment { pub content_disposition: Option, #[serde(skip_serializing_if = "Option::is_none")] pub content_id: Option, - /// Base64 encoded content of attachment. + /// Base64 encoded content of the attachment. The entire request, including the message body and all attachments, is limited to 6 MB. #[serde(skip_serializing_if = "Option::is_none")] pub content: Option, - /// URL to the attachment. + /// URL that AgentMail can download without custom authentication headers or cookies. + /// Redirects and pre-signed URLs are supported, and the final response must be a + /// successful 2xx response. Keep URL-backed attachments around 30 MB total per message. #[serde(skip_serializing_if = "Option::is_none")] pub url: Option, } diff --git a/cli/agentmail/openapi0.json b/cli/agentmail/openapi0.json index ce6da31..300a893 100644 --- a/cli/agentmail/openapi0.json +++ b/cli/agentmail/openapi0.json @@ -1 +1 @@ -{"openapi":"3.0.1","info":{"title":"AgentMail","version":""},"paths":{"/v0/inboxes":{"get":{"description":"**CLI:**\n```bash\nagentmail inboxes list\n```","operationId":"inboxes_list","tags":["Inboxes"],"parameters":[{"name":"limit","in":"query","required":false,"schema":{"$ref":"#/components/schemas/Limit","nullable":true}},{"name":"page_token","in":"query","required":false,"schema":{"$ref":"#/components/schemas/PageToken","nullable":true}},{"name":"ascending","in":"query","required":false,"schema":{"$ref":"#/components/schemas/Ascending","nullable":true}}],"responses":{"200":{"description":"","content":{"application/json":{"schema":{"$ref":"#/components/schemas/inboxesListInboxesResponse"}}}}},"summary":"List Inboxes","servers":[{"url":"https://api.agentmail.to"},{"url":"https://x402.api.agentmail.to"},{"url":"https://mpp.api.agentmail.to"},{"url":"https://api.agentmail.eu"}],"security":[{"BearerAuth":[]},{"TokenAuth":[]}],"x-fern-sdk-group-name":["inboxes"],"x-fern-sdk-method-name":"list"},"post":{"description":"**CLI:**\n```bash\nagentmail inboxes create --display-name \"My Agent\" --username myagent --domain agentmail.to\n```","operationId":"inboxes_create","tags":["Inboxes"],"parameters":[],"responses":{"200":{"description":"","content":{"application/json":{"schema":{"$ref":"#/components/schemas/inboxesInbox"}}}},"400":{"description":"","content":{"application/json":{"schema":{"$ref":"#/components/schemas/ValidationErrorResponse"}}}},"422":{"description":"","content":{"application/json":{"schema":{"$ref":"#/components/schemas/ErrorResponse"}}}}},"summary":"Create Inbox","servers":[{"url":"https://api.agentmail.to"},{"url":"https://x402.api.agentmail.to"},{"url":"https://mpp.api.agentmail.to"},{"url":"https://api.agentmail.eu"}],"security":[{"BearerAuth":[]},{"TokenAuth":[]}],"requestBody":{"required":false,"content":{"application/json":{"schema":{"$ref":"#/components/schemas/inboxesCreateInboxRequest","nullable":true}}}},"x-fern-sdk-group-name":["inboxes"],"x-fern-sdk-method-name":"create"}},"/v0/inboxes/{inbox_id}":{"get":{"description":"**CLI:**\n```bash\nagentmail inboxes get --inbox-id \n```","operationId":"inboxes_get","tags":["Inboxes"],"parameters":[{"name":"inbox_id","in":"path","required":true,"schema":{"$ref":"#/components/schemas/inboxesInboxId"}}],"responses":{"200":{"description":"","content":{"application/json":{"schema":{"$ref":"#/components/schemas/inboxesInbox"}}}},"404":{"description":"","content":{"application/json":{"schema":{"$ref":"#/components/schemas/ErrorResponse"}}}}},"summary":"Get Inbox","servers":[{"url":"https://api.agentmail.to"},{"url":"https://x402.api.agentmail.to"},{"url":"https://mpp.api.agentmail.to"},{"url":"https://api.agentmail.eu"}],"security":[{"BearerAuth":[]},{"TokenAuth":[]}],"x-fern-sdk-group-name":["inboxes"],"x-fern-sdk-method-name":"get"},"patch":{"description":"**CLI:**\n```bash\nagentmail inboxes update --inbox-id --display-name \"Updated Name\"\n```","operationId":"inboxes_update","tags":["Inboxes"],"parameters":[{"name":"inbox_id","in":"path","required":true,"schema":{"$ref":"#/components/schemas/inboxesInboxId"}}],"responses":{"200":{"description":"","content":{"application/json":{"schema":{"$ref":"#/components/schemas/inboxesInbox"}}}},"400":{"description":"","content":{"application/json":{"schema":{"$ref":"#/components/schemas/ValidationErrorResponse"}}}},"404":{"description":"","content":{"application/json":{"schema":{"$ref":"#/components/schemas/ErrorResponse"}}}}},"summary":"Update Inbox","servers":[{"url":"https://api.agentmail.to"},{"url":"https://x402.api.agentmail.to"},{"url":"https://mpp.api.agentmail.to"},{"url":"https://api.agentmail.eu"}],"security":[{"BearerAuth":[]},{"TokenAuth":[]}],"requestBody":{"description":"Expects an object; provide at least one of `display_name` or `metadata`.","required":true,"content":{"application/json":{"schema":{"$ref":"#/components/schemas/inboxesUpdateInboxRequest"}}}},"x-fern-sdk-group-name":["inboxes"],"x-fern-sdk-method-name":"update"},"delete":{"description":"**CLI:**\n```bash\nagentmail inboxes delete --inbox-id \n```","operationId":"inboxes_delete","tags":["Inboxes"],"parameters":[{"name":"inbox_id","in":"path","required":true,"schema":{"$ref":"#/components/schemas/inboxesInboxId"}}],"responses":{"204":{"description":""},"404":{"description":"","content":{"application/json":{"schema":{"$ref":"#/components/schemas/ErrorResponse"}}}}},"summary":"Delete Inbox","servers":[{"url":"https://api.agentmail.to"},{"url":"https://x402.api.agentmail.to"},{"url":"https://mpp.api.agentmail.to"},{"url":"https://api.agentmail.eu"}],"security":[{"BearerAuth":[]},{"TokenAuth":[]}],"x-fern-sdk-group-name":["inboxes"],"x-fern-sdk-method-name":"delete"}},"/v0/pods":{"get":{"description":"**CLI:**\n```bash\nagentmail pods list\n```","operationId":"pods_list","tags":["Pods"],"parameters":[{"name":"limit","in":"query","required":false,"schema":{"$ref":"#/components/schemas/Limit","nullable":true}},{"name":"page_token","in":"query","required":false,"schema":{"$ref":"#/components/schemas/PageToken","nullable":true}},{"name":"ascending","in":"query","required":false,"schema":{"$ref":"#/components/schemas/Ascending","nullable":true}}],"responses":{"200":{"description":"","content":{"application/json":{"schema":{"$ref":"#/components/schemas/podsListPodsResponse"}}}}},"summary":"List Pods","servers":[{"url":"https://api.agentmail.to"},{"url":"https://x402.api.agentmail.to"},{"url":"https://mpp.api.agentmail.to"},{"url":"https://api.agentmail.eu"}],"security":[{"BearerAuth":[]},{"TokenAuth":[]}],"x-fern-sdk-group-name":["pods"],"x-fern-sdk-method-name":"list"},"post":{"description":"**CLI:**\n```bash\nagentmail pods create --client-id my-pod\n```","operationId":"pods_create","tags":["Pods"],"parameters":[],"responses":{"200":{"description":"","content":{"application/json":{"schema":{"$ref":"#/components/schemas/podsPod"}}}},"400":{"description":"","content":{"application/json":{"schema":{"$ref":"#/components/schemas/ValidationErrorResponse"}}}}},"summary":"Create Pod","servers":[{"url":"https://api.agentmail.to"},{"url":"https://x402.api.agentmail.to"},{"url":"https://mpp.api.agentmail.to"},{"url":"https://api.agentmail.eu"}],"security":[{"BearerAuth":[]},{"TokenAuth":[]}],"requestBody":{"required":true,"content":{"application/json":{"schema":{"$ref":"#/components/schemas/podsCreatePodRequest"}}}},"x-fern-sdk-group-name":["pods"],"x-fern-sdk-method-name":"create"}},"/v0/pods/{pod_id}":{"get":{"description":"**CLI:**\n```bash\nagentmail pods get --pod-id \n```","operationId":"pods_get","tags":["Pods"],"parameters":[{"name":"pod_id","in":"path","required":true,"schema":{"$ref":"#/components/schemas/podsPodId"}}],"responses":{"200":{"description":"","content":{"application/json":{"schema":{"$ref":"#/components/schemas/podsPod"}}}},"404":{"description":"","content":{"application/json":{"schema":{"$ref":"#/components/schemas/ErrorResponse"}}}}},"summary":"Get Pod","servers":[{"url":"https://api.agentmail.to"},{"url":"https://x402.api.agentmail.to"},{"url":"https://mpp.api.agentmail.to"},{"url":"https://api.agentmail.eu"}],"security":[{"BearerAuth":[]},{"TokenAuth":[]}],"x-fern-sdk-group-name":["pods"],"x-fern-sdk-method-name":"get"},"delete":{"description":"**CLI:**\n```bash\nagentmail pods delete --pod-id \n```","operationId":"pods_delete","tags":["Pods"],"parameters":[{"name":"pod_id","in":"path","required":true,"schema":{"$ref":"#/components/schemas/podsPodId"}}],"responses":{"204":{"description":""},"404":{"description":"","content":{"application/json":{"schema":{"$ref":"#/components/schemas/ErrorResponse"}}}}},"summary":"Delete Pod","servers":[{"url":"https://api.agentmail.to"},{"url":"https://x402.api.agentmail.to"},{"url":"https://mpp.api.agentmail.to"},{"url":"https://api.agentmail.eu"}],"security":[{"BearerAuth":[]},{"TokenAuth":[]}],"x-fern-sdk-group-name":["pods"],"x-fern-sdk-method-name":"delete"}},"/v0/webhooks":{"get":{"description":"**CLI:**\n```bash\nagentmail webhooks list\n```","operationId":"webhooks_list","tags":["Webhooks"],"parameters":[{"name":"limit","in":"query","required":false,"schema":{"$ref":"#/components/schemas/Limit","nullable":true}},{"name":"page_token","in":"query","required":false,"schema":{"$ref":"#/components/schemas/PageToken","nullable":true}},{"name":"ascending","in":"query","required":false,"schema":{"$ref":"#/components/schemas/Ascending","nullable":true}}],"responses":{"200":{"description":"","content":{"application/json":{"schema":{"$ref":"#/components/schemas/webhooksListWebhooksResponse"}}}}},"summary":"List Webhooks","servers":[{"url":"https://api.agentmail.to"},{"url":"https://x402.api.agentmail.to"},{"url":"https://mpp.api.agentmail.to"},{"url":"https://api.agentmail.eu"}],"security":[{"BearerAuth":[]},{"TokenAuth":[]}],"x-fern-sdk-group-name":["webhooks"],"x-fern-sdk-method-name":"list"},"post":{"description":"**CLI:**\n```bash\nagentmail webhooks create --url https://example.com/webhook --event-types message.received\n```","operationId":"webhooks_create","tags":["Webhooks"],"parameters":[],"responses":{"200":{"description":"","content":{"application/json":{"schema":{"$ref":"#/components/schemas/webhooksWebhook"}}}},"400":{"description":"","content":{"application/json":{"schema":{"$ref":"#/components/schemas/ValidationErrorResponse"}}}}},"summary":"Create Webhook","servers":[{"url":"https://api.agentmail.to"},{"url":"https://x402.api.agentmail.to"},{"url":"https://mpp.api.agentmail.to"},{"url":"https://api.agentmail.eu"}],"security":[{"BearerAuth":[]},{"TokenAuth":[]}],"requestBody":{"required":true,"content":{"application/json":{"schema":{"$ref":"#/components/schemas/webhooksCreateWebhookRequest"}}}},"x-fern-sdk-group-name":["webhooks"],"x-fern-sdk-method-name":"create"}},"/v0/webhooks/{webhook_id}":{"get":{"description":"**CLI:**\n```bash\nagentmail webhooks get --webhook-id \n```","operationId":"webhooks_get","tags":["Webhooks"],"parameters":[{"name":"webhook_id","in":"path","required":true,"schema":{"$ref":"#/components/schemas/webhooksWebhookId"}}],"responses":{"200":{"description":"","content":{"application/json":{"schema":{"$ref":"#/components/schemas/webhooksWebhook"}}}},"404":{"description":"","content":{"application/json":{"schema":{"$ref":"#/components/schemas/ErrorResponse"}}}}},"summary":"Get Webhook","servers":[{"url":"https://api.agentmail.to"},{"url":"https://x402.api.agentmail.to"},{"url":"https://mpp.api.agentmail.to"},{"url":"https://api.agentmail.eu"}],"security":[{"BearerAuth":[]},{"TokenAuth":[]}],"x-fern-sdk-group-name":["webhooks"],"x-fern-sdk-method-name":"get"},"patch":{"description":"Update inbox or pod subscriptions, or replace the webhook's `event_types` in full when you pass a\nnon-empty `event_types` array (see request field docs). Inbox and pod changes use add/remove lists.\n\n**CLI:**\n```bash\nagentmail webhooks update --webhook-id --add-inbox-ids \n```","operationId":"webhooks_update","tags":["Webhooks"],"parameters":[{"name":"webhook_id","in":"path","required":true,"schema":{"$ref":"#/components/schemas/webhooksWebhookId"}}],"responses":{"200":{"description":"","content":{"application/json":{"schema":{"$ref":"#/components/schemas/webhooksWebhook"}}}},"400":{"description":"","content":{"application/json":{"schema":{"$ref":"#/components/schemas/ValidationErrorResponse"}}}},"404":{"description":"","content":{"application/json":{"schema":{"$ref":"#/components/schemas/ErrorResponse"}}}}},"summary":"Update Webhook","servers":[{"url":"https://api.agentmail.to"},{"url":"https://x402.api.agentmail.to"},{"url":"https://mpp.api.agentmail.to"},{"url":"https://api.agentmail.eu"}],"security":[{"BearerAuth":[]},{"TokenAuth":[]}],"requestBody":{"required":true,"content":{"application/json":{"schema":{"$ref":"#/components/schemas/webhooksUpdateWebhookRequest"}}}},"x-fern-sdk-group-name":["webhooks"],"x-fern-sdk-method-name":"update"},"delete":{"description":"**CLI:**\n```bash\nagentmail webhooks delete --webhook-id \n```","operationId":"webhooks_delete","tags":["Webhooks"],"parameters":[{"name":"webhook_id","in":"path","required":true,"schema":{"$ref":"#/components/schemas/webhooksWebhookId"}}],"responses":{"204":{"description":""},"404":{"description":"","content":{"application/json":{"schema":{"$ref":"#/components/schemas/ErrorResponse"}}}}},"summary":"Delete Webhook","servers":[{"url":"https://api.agentmail.to"},{"url":"https://x402.api.agentmail.to"},{"url":"https://mpp.api.agentmail.to"},{"url":"https://api.agentmail.eu"}],"security":[{"BearerAuth":[]},{"TokenAuth":[]}],"x-fern-sdk-group-name":["webhooks"],"x-fern-sdk-method-name":"delete"}},"/v0/webhooks/{webhook_id}/headers":{"get":{"description":"List the names of custom HTTP headers included with deliveries to this webhook. Header values are\nwrite-only and are never returned.","operationId":"webhooks_getHeaders","tags":["Webhooks"],"parameters":[{"name":"webhook_id","in":"path","required":true,"schema":{"$ref":"#/components/schemas/webhooksWebhookId"}}],"responses":{"200":{"description":"","content":{"application/json":{"schema":{"$ref":"#/components/schemas/webhooksWebhookHeaderNamesResponse"}}}},"404":{"description":"","content":{"application/json":{"schema":{"$ref":"#/components/schemas/ErrorResponse"}}}}},"summary":"Get Webhook Headers","servers":[{"url":"https://api.agentmail.to"},{"url":"https://x402.api.agentmail.to"},{"url":"https://mpp.api.agentmail.to"},{"url":"https://api.agentmail.eu"}],"security":[{"BearerAuth":[]},{"TokenAuth":[]}],"x-fern-sdk-group-name":["webhooks"],"x-fern-sdk-method-name":"get-headers"},"patch":{"description":"Atomically set, replace, or remove custom HTTP headers included with deliveries to this webhook.\nHeader values remain write-only.","operationId":"webhooks_updateHeaders","tags":["Webhooks"],"parameters":[{"name":"webhook_id","in":"path","required":true,"schema":{"$ref":"#/components/schemas/webhooksWebhookId"}}],"responses":{"204":{"description":""},"400":{"description":"","content":{"application/json":{"schema":{"$ref":"#/components/schemas/ValidationErrorResponse"}}}},"404":{"description":"","content":{"application/json":{"schema":{"$ref":"#/components/schemas/ErrorResponse"}}}}},"summary":"Update Webhook Headers","servers":[{"url":"https://api.agentmail.to"},{"url":"https://x402.api.agentmail.to"},{"url":"https://mpp.api.agentmail.to"},{"url":"https://api.agentmail.eu"}],"security":[{"BearerAuth":[]},{"TokenAuth":[]}],"requestBody":{"required":true,"content":{"application/json":{"schema":{"$ref":"#/components/schemas/webhooksUpdateWebhookHeadersRequest"}}}},"x-fern-sdk-group-name":["webhooks"],"x-fern-sdk-method-name":"update-headers"}},"/v0/agent/sign-up":{"post":{"description":"Create a new agent organization with an inbox and API key. This endpoint is for signing up for the first time. If you've already signed up, you're all set — just use your existing API key.\n\nA 6-digit OTP is sent to the human's email for verification.\n\nThis endpoint is idempotent. Calling it again with the same `human_email` will rotate the API key and resend the OTP if expired.\n\nThe returned API key has limited permissions until the organization is verified via the verify endpoint.\n\n**CLI:**\n```bash\nagentmail agent sign-up --human-email user@example.com --username my-agent\n```","operationId":"agent_signUp","tags":["Agent"],"parameters":[],"responses":{"200":{"description":"","content":{"application/json":{"schema":{"$ref":"#/components/schemas/AgentSignupResponse"}}}},"400":{"description":"","content":{"application/json":{"schema":{"$ref":"#/components/schemas/ValidationErrorResponse"}}}}},"summary":"Sign Up","servers":[{"url":"https://api.agentmail.to"},{"url":"https://x402.api.agentmail.to"},{"url":"https://mpp.api.agentmail.to"},{"url":"https://api.agentmail.eu"}],"requestBody":{"required":true,"content":{"application/json":{"schema":{"$ref":"#/components/schemas/AgentSignupRequest"}}}},"x-fern-sdk-group-name":["agent"],"x-fern-sdk-method-name":"sign-up"}},"/v0/agent/verify":{"post":{"description":"Verify an agent organization using the 6-digit OTP sent to the human's email during sign-up.\n\nOn success, the organization is upgraded from `agent_unverified` to `agent_verified`, the send allowlist is removed, and free plan entitlements are applied.\n\nThe OTP expires after 24 hours and allows a maximum of 10 attempts. If you run into any difficulties receiving the OTP code, you can also create an account on [console.agentmail.to](https://console.agentmail.to) using the human email address you provided to verify your account.\n\n**CLI:**\n```bash\nagentmail agent verify --otp-code 123456\n```","operationId":"agent_verify","tags":["Agent"],"parameters":[],"responses":{"200":{"description":"","content":{"application/json":{"schema":{"$ref":"#/components/schemas/AgentVerifyResponse"}}}}},"summary":"Verify","servers":[{"url":"https://api.agentmail.to"},{"url":"https://x402.api.agentmail.to"},{"url":"https://mpp.api.agentmail.to"},{"url":"https://api.agentmail.eu"}],"security":[{"BearerAuth":[]},{"TokenAuth":[]}],"requestBody":{"required":true,"content":{"application/json":{"schema":{"$ref":"#/components/schemas/AgentVerifyRequest"}}}},"x-fern-sdk-group-name":["agent"],"x-fern-sdk-method-name":"verify"}},"/v0/api-keys":{"get":{"description":"**CLI:**\n```bash\nagentmail api-keys list\n```","operationId":"apiKeys_list","tags":["ApiKeys"],"parameters":[{"name":"limit","in":"query","required":false,"schema":{"$ref":"#/components/schemas/Limit","nullable":true}},{"name":"page_token","in":"query","required":false,"schema":{"$ref":"#/components/schemas/PageToken","nullable":true}},{"name":"ascending","in":"query","required":false,"schema":{"$ref":"#/components/schemas/Ascending","nullable":true}}],"responses":{"200":{"description":"","content":{"application/json":{"schema":{"$ref":"#/components/schemas/ListApiKeysResponse"}}}}},"summary":"List API Keys","servers":[{"url":"https://api.agentmail.to"},{"url":"https://x402.api.agentmail.to"},{"url":"https://mpp.api.agentmail.to"},{"url":"https://api.agentmail.eu"}],"security":[{"BearerAuth":[]},{"TokenAuth":[]}],"x-fern-sdk-group-name":["apiKeys"],"x-fern-sdk-method-name":"list"},"post":{"description":"**CLI:**\n```bash\nagentmail api-keys create --name \"My Key\"\n```","operationId":"apiKeys_create","tags":["ApiKeys"],"parameters":[],"responses":{"200":{"description":"","content":{"application/json":{"schema":{"$ref":"#/components/schemas/CreateApiKeyResponse"}}}},"400":{"description":"","content":{"application/json":{"schema":{"$ref":"#/components/schemas/ValidationErrorResponse"}}}}},"summary":"Create API Key","servers":[{"url":"https://api.agentmail.to"},{"url":"https://x402.api.agentmail.to"},{"url":"https://mpp.api.agentmail.to"},{"url":"https://api.agentmail.eu"}],"security":[{"BearerAuth":[]},{"TokenAuth":[]}],"requestBody":{"required":true,"content":{"application/json":{"schema":{"$ref":"#/components/schemas/CreateApiKeyRequest"}}}},"x-fern-sdk-group-name":["apiKeys"],"x-fern-sdk-method-name":"create"}},"/v0/api-keys/{api_key_id}":{"delete":{"description":"**CLI:**\n```bash\nagentmail api-keys delete --api-key-id \n```","operationId":"apiKeys_delete","tags":["ApiKeys"],"parameters":[{"name":"api_key_id","in":"path","required":true,"schema":{"$ref":"#/components/schemas/ApiKeyId"}}],"responses":{"204":{"description":""},"404":{"description":"","content":{"application/json":{"schema":{"$ref":"#/components/schemas/ErrorResponse"}}}}},"summary":"Delete API Key","servers":[{"url":"https://api.agentmail.to"},{"url":"https://x402.api.agentmail.to"},{"url":"https://mpp.api.agentmail.to"},{"url":"https://api.agentmail.eu"}],"security":[{"BearerAuth":[]},{"TokenAuth":[]}],"x-fern-sdk-group-name":["apiKeys"],"x-fern-sdk-method-name":"delete"}},"/v0/api-keys/public-keys":{"get":{"description":"List only public-key credentials visible to the bearer caller's scope.\nBearer credentials are never returned, even though both credential types\nshare storage and pagination indexes. Requires `api_key_read`.","operationId":"apiKeys_listPublicKeys","tags":["ApiKeys"],"parameters":[{"name":"limit","in":"query","required":false,"schema":{"$ref":"#/components/schemas/Limit","nullable":true}},{"name":"page_token","in":"query","required":false,"schema":{"$ref":"#/components/schemas/PageToken","nullable":true}},{"name":"ascending","in":"query","required":false,"schema":{"$ref":"#/components/schemas/Ascending","nullable":true}}],"responses":{"200":{"description":"","content":{"application/json":{"schema":{"$ref":"#/components/schemas/ListPublicKeysResponse"}}}}},"summary":"List Public-Key Credentials","servers":[{"url":"https://api.agentmail.to"},{"url":"https://x402.api.agentmail.to"},{"url":"https://mpp.api.agentmail.to"},{"url":"https://api.agentmail.eu"}],"security":[{"BearerAuth":[]},{"TokenAuth":[]}],"x-fern-sdk-group-name":["apiKeys"],"x-fern-sdk-method-name":"list-public-keys"},"post":{"description":"Register a public P-256 JWK using an existing AgentMail bearer API key\nwith `api_key_create`. Re-registering the same JWK creates a new\ncredential ID; it does not replace or recover an earlier credential.\nThe private key must never be sent to AgentMail.","operationId":"apiKeys_createPublicKey","tags":["ApiKeys"],"parameters":[],"responses":{"200":{"description":"","content":{"application/json":{"schema":{"$ref":"#/components/schemas/PublicKeyCredential"}}}},"400":{"description":"","content":{"application/json":{"schema":{"$ref":"#/components/schemas/ValidationErrorResponse"}}}},"409":{"description":"","content":{"application/json":{"schema":{"$ref":"#/components/schemas/ErrorResponse"}}}}},"summary":"Register Public-Key Credential","servers":[{"url":"https://api.agentmail.to"},{"url":"https://x402.api.agentmail.to"},{"url":"https://mpp.api.agentmail.to"},{"url":"https://api.agentmail.eu"}],"security":[{"BearerAuth":[]},{"TokenAuth":[]}],"requestBody":{"required":true,"content":{"application/json":{"schema":{"$ref":"#/components/schemas/CreatePublicKeyRequest"}}}},"x-fern-sdk-group-name":["apiKeys"],"x-fern-sdk-method-name":"create-public-key"}},"/v0/api-keys/public-keys/{api_key_id}":{"patch":{"description":"Rename the credential. All security-relevant fields are immutable.\nRequires `api_key_update`.","operationId":"apiKeys_updatePublicKeyName","tags":["ApiKeys"],"parameters":[{"name":"api_key_id","in":"path","description":"Public-key credential ID returned by registration.","required":true,"schema":{"type":"string","format":"uuid"}}],"responses":{"200":{"description":"","content":{"application/json":{"schema":{"$ref":"#/components/schemas/PublicKeyCredential"}}}},"400":{"description":"","content":{"application/json":{"schema":{"$ref":"#/components/schemas/ValidationErrorResponse"}}}},"404":{"description":"","content":{"application/json":{"schema":{"$ref":"#/components/schemas/ErrorResponse"}}}}},"summary":"Rename Public-Key Credential","servers":[{"url":"https://api.agentmail.to"},{"url":"https://x402.api.agentmail.to"},{"url":"https://mpp.api.agentmail.to"},{"url":"https://api.agentmail.eu"}],"security":[{"BearerAuth":[]},{"TokenAuth":[]}],"requestBody":{"required":true,"content":{"application/json":{"schema":{"$ref":"#/components/schemas/UpdatePublicKeyNameRequest"}}}},"x-fern-sdk-group-name":["apiKeys"],"x-fern-sdk-method-name":"update-public-key-name"},"delete":{"description":"Permanently revoke one public-key credential. This hard-deletes the\ncredential; repeating the request returns not found. Requires\n`api_key_delete`.","operationId":"apiKeys_revokePublicKey","tags":["ApiKeys"],"parameters":[{"name":"api_key_id","in":"path","description":"Public-key credential ID returned by registration.","required":true,"schema":{"type":"string","format":"uuid"}}],"responses":{"204":{"description":""},"404":{"description":"","content":{"application/json":{"schema":{"$ref":"#/components/schemas/ErrorResponse"}}}}},"summary":"Revoke Public-Key Credential","servers":[{"url":"https://api.agentmail.to"},{"url":"https://x402.api.agentmail.to"},{"url":"https://mpp.api.agentmail.to"},{"url":"https://api.agentmail.eu"}],"security":[{"BearerAuth":[]},{"TokenAuth":[]}],"x-fern-sdk-group-name":["apiKeys"],"x-fern-sdk-method-name":"revoke-public-key"}},"/v0/api-keys/public-keys/agentid-sign-in/revoke-all":{"post":{"description":"Invalidate every current public-key credential in the caller's\norganization by advancing its AgentID key generation. The caller must be\norganization-scoped and either have `api_key_delete` or, for a verified\nself-serve agent organization, use an unrestricted unmanaged bearer\ncredential. No request body is accepted.\n\n`Idempotency-Key` is required and must be a UUID. Reusing the same UUID\nreturns the original permanent receipt without advancing the generation\nagain. A new UUID performs a new generation advance.","operationId":"apiKeys_revokeAllAgentIdSignInKeys","tags":["ApiKeys"],"parameters":[{"name":"Idempotency-Key","in":"header","description":"Required UUID identifying this revoke-all operation permanently.","required":true,"schema":{"type":"string","format":"uuid"}}],"responses":{"200":{"description":"","content":{"application/json":{"schema":{"$ref":"#/components/schemas/RevokeAllAgentIdSignInKeysResponse"}}}},"400":{"description":"","content":{"application/json":{"schema":{"$ref":"#/components/schemas/ValidationErrorResponse"}}}},"409":{"description":"","content":{"application/json":{"schema":{"$ref":"#/components/schemas/ErrorResponse"}}}}},"summary":"Revoke All AgentID Sign-In Keys","servers":[{"url":"https://api.agentmail.to"},{"url":"https://x402.api.agentmail.to"},{"url":"https://mpp.api.agentmail.to"},{"url":"https://api.agentmail.eu"}],"security":[{"BearerAuth":[]},{"TokenAuth":[]}],"x-fern-sdk-group-name":["apiKeys"],"x-fern-sdk-method-name":"revoke-all-agent-id-sign-in-keys"}},"/v0/auth/me":{"get":{"description":"Returns the identity and scope of the authenticated credential. Useful when a client holds a pod-scoped or inbox-scoped API key and needs to discover the parent organization, pod, or inbox without prior knowledge.\n\n**CLI:**\n```bash\nagentmail auth me\n```","operationId":"auth_me","tags":["Auth"],"parameters":[],"responses":{"200":{"description":"","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Identity"}}}}},"summary":"Who Am I","servers":[{"url":"https://api.agentmail.to"},{"url":"https://x402.api.agentmail.to"},{"url":"https://mpp.api.agentmail.to"},{"url":"https://api.agentmail.eu"}],"security":[{"BearerAuth":[]},{"TokenAuth":[]}],"x-fern-sdk-group-name":["auth"],"x-fern-sdk-method-name":"me"}},"/v0/domains":{"get":{"description":"**CLI:**\n```bash\nagentmail domains list\n```","operationId":"domains_list","tags":["Domains"],"parameters":[{"name":"limit","in":"query","required":false,"schema":{"$ref":"#/components/schemas/Limit","nullable":true}},{"name":"page_token","in":"query","required":false,"schema":{"$ref":"#/components/schemas/PageToken","nullable":true}},{"name":"ascending","in":"query","required":false,"schema":{"$ref":"#/components/schemas/Ascending","nullable":true}}],"responses":{"200":{"description":"","content":{"application/json":{"schema":{"$ref":"#/components/schemas/ListDomainsResponse"}}}}},"summary":"List Domains","servers":[{"url":"https://api.agentmail.to"},{"url":"https://x402.api.agentmail.to"},{"url":"https://mpp.api.agentmail.to"},{"url":"https://api.agentmail.eu"}],"security":[{"BearerAuth":[]},{"TokenAuth":[]}],"x-fern-sdk-group-name":["domains"],"x-fern-sdk-method-name":"list"},"post":{"description":"**CLI:**\n```bash\nagentmail domains create --domain example.com\n```","operationId":"domains_create","tags":["Domains"],"parameters":[],"responses":{"200":{"description":"","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Domain"}}}},"400":{"description":"","content":{"application/json":{"schema":{"$ref":"#/components/schemas/ValidationErrorResponse"}}}}},"summary":"Create Domain","servers":[{"url":"https://api.agentmail.to"},{"url":"https://x402.api.agentmail.to"},{"url":"https://mpp.api.agentmail.to"},{"url":"https://api.agentmail.eu"}],"security":[{"BearerAuth":[]},{"TokenAuth":[]}],"requestBody":{"required":true,"content":{"application/json":{"schema":{"$ref":"#/components/schemas/CreateDomainRequest"}}}},"x-fern-sdk-group-name":["domains"],"x-fern-sdk-method-name":"create"}},"/v0/domains/{domain_id}":{"get":{"description":"**CLI:**\n```bash\nagentmail domains get --domain-id \n```","operationId":"domains_get","tags":["Domains"],"parameters":[{"name":"domain_id","in":"path","required":true,"schema":{"$ref":"#/components/schemas/DomainId"}}],"responses":{"200":{"description":"","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Domain"}}}},"404":{"description":"","content":{"application/json":{"schema":{"$ref":"#/components/schemas/ErrorResponse"}}}}},"summary":"Get Domain","servers":[{"url":"https://api.agentmail.to"},{"url":"https://x402.api.agentmail.to"},{"url":"https://mpp.api.agentmail.to"},{"url":"https://api.agentmail.eu"}],"security":[{"BearerAuth":[]},{"TokenAuth":[]}],"x-fern-sdk-group-name":["domains"],"x-fern-sdk-method-name":"get"},"patch":{"description":"**CLI:**\n```bash\nagentmail domains update --domain-id \n```","operationId":"domains_update","tags":["Domains"],"parameters":[{"name":"domain_id","in":"path","required":true,"schema":{"$ref":"#/components/schemas/DomainId"}}],"responses":{"200":{"description":"","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Domain"}}}},"404":{"description":"","content":{"application/json":{"schema":{"$ref":"#/components/schemas/ErrorResponse"}}}}},"summary":"Update Domain","servers":[{"url":"https://api.agentmail.to"},{"url":"https://x402.api.agentmail.to"},{"url":"https://mpp.api.agentmail.to"},{"url":"https://api.agentmail.eu"}],"security":[{"BearerAuth":[]},{"TokenAuth":[]}],"requestBody":{"required":true,"content":{"application/json":{"schema":{"$ref":"#/components/schemas/UpdateDomainRequest"}}}},"x-fern-sdk-group-name":["domains"],"x-fern-sdk-method-name":"update"},"delete":{"description":"**CLI:**\n```bash\nagentmail domains delete --domain-id \n```","operationId":"domains_delete","tags":["Domains"],"parameters":[{"name":"domain_id","in":"path","required":true,"schema":{"$ref":"#/components/schemas/DomainId"}}],"responses":{"204":{"description":""},"404":{"description":"","content":{"application/json":{"schema":{"$ref":"#/components/schemas/ErrorResponse"}}}}},"summary":"Delete Domain","servers":[{"url":"https://api.agentmail.to"},{"url":"https://x402.api.agentmail.to"},{"url":"https://mpp.api.agentmail.to"},{"url":"https://api.agentmail.eu"}],"security":[{"BearerAuth":[]},{"TokenAuth":[]}],"x-fern-sdk-group-name":["domains"],"x-fern-sdk-method-name":"delete"}},"/v0/domains/{domain_id}/zone-file":{"get":{"description":"**CLI:**\n```bash\nagentmail domains get-zone-file --domain-id \n```","operationId":"domains_getZoneFile","tags":["Domains"],"parameters":[{"name":"domain_id","in":"path","required":true,"schema":{"$ref":"#/components/schemas/DomainId"}}],"responses":{"204":{"description":""},"404":{"description":"","content":{"application/json":{"schema":{"$ref":"#/components/schemas/ErrorResponse"}}}}},"summary":"Get Zone File","servers":[{"url":"https://api.agentmail.to"},{"url":"https://x402.api.agentmail.to"},{"url":"https://mpp.api.agentmail.to"},{"url":"https://api.agentmail.eu"}],"security":[{"BearerAuth":[]},{"TokenAuth":[]}],"x-fern-sdk-group-name":["domains"],"x-fern-sdk-method-name":"get-zone-file"}},"/v0/domains/{domain_id}/verify":{"post":{"description":"**CLI:**\n```bash\nagentmail domains verify --domain-id \n```","operationId":"domains_verify","tags":["Domains"],"parameters":[{"name":"domain_id","in":"path","required":true,"schema":{"$ref":"#/components/schemas/DomainId"}}],"responses":{"204":{"description":""},"404":{"description":"","content":{"application/json":{"schema":{"$ref":"#/components/schemas/ErrorResponse"}}}}},"summary":"Verify Domain","servers":[{"url":"https://api.agentmail.to"},{"url":"https://x402.api.agentmail.to"},{"url":"https://mpp.api.agentmail.to"},{"url":"https://api.agentmail.eu"}],"security":[{"BearerAuth":[]},{"TokenAuth":[]}],"x-fern-sdk-group-name":["domains"],"x-fern-sdk-method-name":"verify"}},"/v0/drafts":{"get":{"description":"**CLI:**\n```bash\nagentmail drafts list\n```","operationId":"drafts_list","tags":["Drafts"],"parameters":[{"name":"limit","in":"query","required":false,"schema":{"$ref":"#/components/schemas/Limit","nullable":true}},{"name":"page_token","in":"query","required":false,"schema":{"$ref":"#/components/schemas/PageToken","nullable":true}},{"name":"labels","in":"query","required":false,"schema":{"$ref":"#/components/schemas/Labels","nullable":true}},{"name":"before","in":"query","required":false,"schema":{"$ref":"#/components/schemas/Before","nullable":true}},{"name":"after","in":"query","required":false,"schema":{"$ref":"#/components/schemas/After","nullable":true}},{"name":"ascending","in":"query","required":false,"schema":{"$ref":"#/components/schemas/Ascending","nullable":true}}],"responses":{"200":{"description":"","content":{"application/json":{"schema":{"$ref":"#/components/schemas/ListDraftsResponse"}}}},"404":{"description":"","content":{"application/json":{"schema":{"$ref":"#/components/schemas/ErrorResponse"}}}}},"summary":"List Drafts","servers":[{"url":"https://api.agentmail.to"},{"url":"https://x402.api.agentmail.to"},{"url":"https://mpp.api.agentmail.to"},{"url":"https://api.agentmail.eu"}],"security":[{"BearerAuth":[]},{"TokenAuth":[]}],"x-fern-sdk-group-name":["drafts"],"x-fern-sdk-method-name":"list"}},"/v0/drafts/{draft_id}":{"get":{"description":"**CLI:**\n```bash\nagentmail drafts get --draft-id \n```","operationId":"drafts_get","tags":["Drafts"],"parameters":[{"name":"draft_id","in":"path","required":true,"schema":{"$ref":"#/components/schemas/DraftId"}}],"responses":{"200":{"description":"","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Draft"}}}},"404":{"description":"","content":{"application/json":{"schema":{"$ref":"#/components/schemas/ErrorResponse"}}}}},"summary":"Get Draft","servers":[{"url":"https://api.agentmail.to"},{"url":"https://x402.api.agentmail.to"},{"url":"https://mpp.api.agentmail.to"},{"url":"https://api.agentmail.eu"}],"security":[{"BearerAuth":[]},{"TokenAuth":[]}],"x-fern-sdk-group-name":["drafts"],"x-fern-sdk-method-name":"get"}},"/v0/drafts/{draft_id}/attachments/{attachment_id}":{"get":{"description":"**CLI:**\n```bash\nagentmail drafts get-attachment --draft-id --attachment-id \n```","operationId":"drafts_getAttachment","tags":["Drafts"],"parameters":[{"name":"draft_id","in":"path","required":true,"schema":{"$ref":"#/components/schemas/DraftId"}},{"name":"attachment_id","in":"path","required":true,"schema":{"$ref":"#/components/schemas/AttachmentId"}}],"responses":{"200":{"description":"","content":{"application/json":{"schema":{"$ref":"#/components/schemas/AttachmentResponse"}}}},"404":{"description":"","content":{"application/json":{"schema":{"$ref":"#/components/schemas/ErrorResponse"}}}}},"summary":"Get Attachment","servers":[{"url":"https://api.agentmail.to"},{"url":"https://x402.api.agentmail.to"},{"url":"https://mpp.api.agentmail.to"},{"url":"https://api.agentmail.eu"}],"security":[{"BearerAuth":[]},{"TokenAuth":[]}],"x-fern-sdk-group-name":["drafts"],"x-fern-sdk-method-name":"get-attachment"}},"/v0/inboxes/{inbox_id}/api-keys":{"get":{"description":"**CLI:**\n```bash\nagentmail inboxes api-keys list --inbox-id \n```","operationId":"inboxes_apiKeys_list","tags":["InboxesApiKeys"],"parameters":[{"name":"inbox_id","in":"path","required":true,"schema":{"$ref":"#/components/schemas/inboxesInboxId"}},{"name":"limit","in":"query","required":false,"schema":{"$ref":"#/components/schemas/Limit","nullable":true}},{"name":"page_token","in":"query","required":false,"schema":{"$ref":"#/components/schemas/PageToken","nullable":true}}],"responses":{"200":{"description":"","content":{"application/json":{"schema":{"$ref":"#/components/schemas/ListApiKeysResponse"}}}},"404":{"description":"","content":{"application/json":{"schema":{"$ref":"#/components/schemas/ErrorResponse"}}}}},"summary":"List API Keys","servers":[{"url":"https://api.agentmail.to"},{"url":"https://x402.api.agentmail.to"},{"url":"https://mpp.api.agentmail.to"},{"url":"https://api.agentmail.eu"}],"security":[{"BearerAuth":[]},{"TokenAuth":[]}],"x-fern-sdk-group-name":["inboxes","apiKeys"],"x-fern-sdk-method-name":"list"},"post":{"description":"**CLI:**\n```bash\nagentmail inboxes api-keys create --inbox-id --name \"My Key\"\n```","operationId":"inboxes_apiKeys_create","tags":["InboxesApiKeys"],"parameters":[{"name":"inbox_id","in":"path","required":true,"schema":{"$ref":"#/components/schemas/inboxesInboxId"}}],"responses":{"200":{"description":"","content":{"application/json":{"schema":{"$ref":"#/components/schemas/CreateApiKeyResponse"}}}},"400":{"description":"","content":{"application/json":{"schema":{"$ref":"#/components/schemas/ValidationErrorResponse"}}}},"404":{"description":"","content":{"application/json":{"schema":{"$ref":"#/components/schemas/ErrorResponse"}}}}},"summary":"Create API Key","servers":[{"url":"https://api.agentmail.to"},{"url":"https://x402.api.agentmail.to"},{"url":"https://mpp.api.agentmail.to"},{"url":"https://api.agentmail.eu"}],"security":[{"BearerAuth":[]},{"TokenAuth":[]}],"requestBody":{"required":true,"content":{"application/json":{"schema":{"$ref":"#/components/schemas/CreateApiKeyRequest"}}}},"x-fern-sdk-group-name":["inboxes","apiKeys"],"x-fern-sdk-method-name":"create"}},"/v0/inboxes/{inbox_id}/api-keys/{api_key_id}":{"delete":{"description":"**CLI:**\n```bash\nagentmail inboxes api-keys delete --inbox-id --api-key-id \n```","operationId":"inboxes_apiKeys_delete","tags":["InboxesApiKeys"],"parameters":[{"name":"inbox_id","in":"path","required":true,"schema":{"$ref":"#/components/schemas/inboxesInboxId"}},{"name":"api_key_id","in":"path","required":true,"schema":{"$ref":"#/components/schemas/ApiKeyId"}}],"responses":{"204":{"description":""},"404":{"description":"","content":{"application/json":{"schema":{"$ref":"#/components/schemas/ErrorResponse"}}}}},"summary":"Delete API Key","servers":[{"url":"https://api.agentmail.to"},{"url":"https://x402.api.agentmail.to"},{"url":"https://mpp.api.agentmail.to"},{"url":"https://api.agentmail.eu"}],"security":[{"BearerAuth":[]},{"TokenAuth":[]}],"x-fern-sdk-group-name":["inboxes","apiKeys"],"x-fern-sdk-method-name":"delete"}},"/v0/inboxes/{inbox_id}/drafts":{"get":{"description":"**CLI:**\n```bash\nagentmail inboxes drafts list --inbox-id \n```","operationId":"inboxes_drafts_list","tags":["InboxesDrafts"],"parameters":[{"name":"inbox_id","in":"path","required":true,"schema":{"$ref":"#/components/schemas/inboxesInboxId"}},{"name":"limit","in":"query","required":false,"schema":{"$ref":"#/components/schemas/Limit","nullable":true}},{"name":"page_token","in":"query","required":false,"schema":{"$ref":"#/components/schemas/PageToken","nullable":true}},{"name":"labels","in":"query","required":false,"schema":{"$ref":"#/components/schemas/Labels","nullable":true}},{"name":"before","in":"query","required":false,"schema":{"$ref":"#/components/schemas/Before","nullable":true}},{"name":"after","in":"query","required":false,"schema":{"$ref":"#/components/schemas/After","nullable":true}},{"name":"ascending","in":"query","required":false,"schema":{"$ref":"#/components/schemas/Ascending","nullable":true}}],"responses":{"200":{"description":"","content":{"application/json":{"schema":{"$ref":"#/components/schemas/ListDraftsResponse"}}}},"404":{"description":"","content":{"application/json":{"schema":{"$ref":"#/components/schemas/ErrorResponse"}}}}},"summary":"List Drafts","servers":[{"url":"https://api.agentmail.to"},{"url":"https://x402.api.agentmail.to"},{"url":"https://mpp.api.agentmail.to"},{"url":"https://api.agentmail.eu"}],"security":[{"BearerAuth":[]},{"TokenAuth":[]}],"x-fern-sdk-group-name":["inboxes","drafts"],"x-fern-sdk-method-name":"list"},"post":{"description":"Create a draft. Supply `in_reply_to` to create a reply draft (with\n`reply_all` to address the whole thread), whose recipients, subject, and\nthreading are derived from the referenced message, or `forward_of` to\ncreate a forward draft, which derives the subject, threading, and\nforwarded content from the source but keeps recipients caller-supplied.\n\n**CLI:**\n```bash\nagentmail inboxes drafts create --inbox-id --to recipient@example.com --subject \"Draft subject\" --text \"Draft body\"\n```","operationId":"inboxes_drafts_create","tags":["InboxesDrafts"],"parameters":[{"name":"inbox_id","in":"path","required":true,"schema":{"$ref":"#/components/schemas/inboxesInboxId"}}],"responses":{"200":{"description":"","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Draft"}}}},"400":{"description":"","content":{"application/json":{"schema":{"$ref":"#/components/schemas/ValidationErrorResponse"}}}},"404":{"description":"","content":{"application/json":{"schema":{"$ref":"#/components/schemas/ErrorResponse"}}}}},"summary":"Create Draft","servers":[{"url":"https://api.agentmail.to"},{"url":"https://x402.api.agentmail.to"},{"url":"https://mpp.api.agentmail.to"},{"url":"https://api.agentmail.eu"}],"security":[{"BearerAuth":[]},{"TokenAuth":[]}],"requestBody":{"required":true,"content":{"application/json":{"schema":{"$ref":"#/components/schemas/CreateDraftRequest"}}}},"x-fern-sdk-group-name":["inboxes","drafts"],"x-fern-sdk-method-name":"create"}},"/v0/inboxes/{inbox_id}/drafts/{draft_id}":{"get":{"description":"**CLI:**\n```bash\nagentmail inboxes drafts get --inbox-id --draft-id \n```","operationId":"inboxes_drafts_get","tags":["InboxesDrafts"],"parameters":[{"name":"inbox_id","in":"path","required":true,"schema":{"$ref":"#/components/schemas/inboxesInboxId"}},{"name":"draft_id","in":"path","required":true,"schema":{"$ref":"#/components/schemas/DraftId"}}],"responses":{"200":{"description":"","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Draft"}}}},"404":{"description":"","content":{"application/json":{"schema":{"$ref":"#/components/schemas/ErrorResponse"}}}}},"summary":"Get Draft","servers":[{"url":"https://api.agentmail.to"},{"url":"https://x402.api.agentmail.to"},{"url":"https://mpp.api.agentmail.to"},{"url":"https://api.agentmail.eu"}],"security":[{"BearerAuth":[]},{"TokenAuth":[]}],"x-fern-sdk-group-name":["inboxes","drafts"],"x-fern-sdk-method-name":"get"},"patch":{"description":"Edit fields on an existing draft. Passing `null` clears a field (or `[]`\nfor a recipient field); `send_at: null` un-schedules a scheduled draft.\nA draft that is already being sent cannot be edited.\n\n**CLI:**\n```bash\nagentmail inboxes drafts update --inbox-id --draft-id --subject \"Updated subject\"\n```","operationId":"inboxes_drafts_update","tags":["InboxesDrafts"],"parameters":[{"name":"inbox_id","in":"path","required":true,"schema":{"$ref":"#/components/schemas/inboxesInboxId"}},{"name":"draft_id","in":"path","required":true,"schema":{"$ref":"#/components/schemas/DraftId"}}],"responses":{"200":{"description":"","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Draft"}}}},"400":{"description":"","content":{"application/json":{"schema":{"$ref":"#/components/schemas/ValidationErrorResponse"}}}},"404":{"description":"","content":{"application/json":{"schema":{"$ref":"#/components/schemas/ErrorResponse"}}}},"409":{"description":"","content":{"application/json":{"schema":{"$ref":"#/components/schemas/ErrorResponse"}}}}},"summary":"Update Draft","servers":[{"url":"https://api.agentmail.to"},{"url":"https://x402.api.agentmail.to"},{"url":"https://mpp.api.agentmail.to"},{"url":"https://api.agentmail.eu"}],"security":[{"BearerAuth":[]},{"TokenAuth":[]}],"requestBody":{"required":true,"content":{"application/json":{"schema":{"$ref":"#/components/schemas/UpdateDraftRequest"}}}},"x-fern-sdk-group-name":["inboxes","drafts"],"x-fern-sdk-method-name":"update"},"delete":{"description":"**CLI:**\n```bash\nagentmail inboxes drafts delete --inbox-id --draft-id \n```","operationId":"inboxes_drafts_delete","tags":["InboxesDrafts"],"parameters":[{"name":"inbox_id","in":"path","required":true,"schema":{"$ref":"#/components/schemas/inboxesInboxId"}},{"name":"draft_id","in":"path","required":true,"schema":{"$ref":"#/components/schemas/DraftId"}}],"responses":{"204":{"description":""},"404":{"description":"","content":{"application/json":{"schema":{"$ref":"#/components/schemas/ErrorResponse"}}}}},"summary":"Delete Draft","servers":[{"url":"https://api.agentmail.to"},{"url":"https://x402.api.agentmail.to"},{"url":"https://mpp.api.agentmail.to"},{"url":"https://api.agentmail.eu"}],"security":[{"BearerAuth":[]},{"TokenAuth":[]}],"x-fern-sdk-group-name":["inboxes","drafts"],"x-fern-sdk-method-name":"delete"}},"/v0/inboxes/{inbox_id}/drafts/{draft_id}/attachments/{attachment_id}":{"get":{"description":"**CLI:**\n```bash\nagentmail inboxes drafts get-attachment --inbox-id --draft-id --attachment-id \n```","operationId":"inboxes_drafts_getAttachment","tags":["InboxesDrafts"],"parameters":[{"name":"inbox_id","in":"path","required":true,"schema":{"$ref":"#/components/schemas/inboxesInboxId"}},{"name":"draft_id","in":"path","required":true,"schema":{"$ref":"#/components/schemas/DraftId"}},{"name":"attachment_id","in":"path","required":true,"schema":{"$ref":"#/components/schemas/AttachmentId"}}],"responses":{"200":{"description":"","content":{"application/json":{"schema":{"$ref":"#/components/schemas/AttachmentResponse"}}}},"404":{"description":"","content":{"application/json":{"schema":{"$ref":"#/components/schemas/ErrorResponse"}}}}},"summary":"Get Attachment","servers":[{"url":"https://api.agentmail.to"},{"url":"https://x402.api.agentmail.to"},{"url":"https://mpp.api.agentmail.to"},{"url":"https://api.agentmail.eu"}],"security":[{"BearerAuth":[]},{"TokenAuth":[]}],"x-fern-sdk-group-name":["inboxes","drafts"],"x-fern-sdk-method-name":"get-attachment"}},"/v0/inboxes/{inbox_id}/drafts/{draft_id}/send":{"post":{"description":"**CLI:**\n```bash\nagentmail inboxes drafts send --inbox-id --draft-id \n```","operationId":"inboxes_drafts_send","tags":["InboxesDrafts"],"parameters":[{"name":"inbox_id","in":"path","required":true,"schema":{"$ref":"#/components/schemas/inboxesInboxId"}},{"name":"draft_id","in":"path","required":true,"schema":{"$ref":"#/components/schemas/DraftId"}},{"name":"Idempotency-Key","in":"header","required":false,"description":"Unique key that makes a send idempotent. A retry carrying the same key returns the original message instead of sending a second email; reusing a key with a different request returns a 409 conflict. Keys expire 24 hours after the send completes.","schema":{"type":"string"}}],"responses":{"200":{"description":"","content":{"application/json":{"schema":{"$ref":"#/components/schemas/SendMessageResponse"}}}},"400":{"description":"","content":{"application/json":{"schema":{"$ref":"#/components/schemas/ValidationErrorResponse"}}}},"403":{"description":"","content":{"application/json":{"schema":{"$ref":"#/components/schemas/ErrorResponse"}}}},"404":{"description":"","content":{"application/json":{"schema":{"$ref":"#/components/schemas/ErrorResponse"}}}},"409":{"description":"","content":{"application/json":{"schema":{"$ref":"#/components/schemas/ErrorResponse"}}}}},"summary":"Send Draft","servers":[{"url":"https://api.agentmail.to"},{"url":"https://x402.api.agentmail.to"},{"url":"https://mpp.api.agentmail.to"},{"url":"https://api.agentmail.eu"}],"security":[{"BearerAuth":[]},{"TokenAuth":[]}],"requestBody":{"required":true,"content":{"application/json":{"schema":{"$ref":"#/components/schemas/UpdateMessageRequest"}}}},"x-fern-sdk-group-name":["inboxes","drafts"],"x-fern-sdk-method-name":"send"}},"/v0/inboxes/{inbox_id}/events":{"get":{"description":"List label change events for an inbox. Returns events in reverse chronological order by default. Use for IMAP UID projection or audit logging.\n\n**CLI:**\n```bash\nagentmail inboxes events list --inbox-id \n```","operationId":"inboxes_events_list","tags":["InboxesEvents"],"parameters":[{"name":"inbox_id","in":"path","required":true,"schema":{"$ref":"#/components/schemas/inboxesInboxId"}},{"name":"limit","in":"query","required":false,"schema":{"$ref":"#/components/schemas/Limit","nullable":true}},{"name":"page_token","in":"query","required":false,"schema":{"$ref":"#/components/schemas/PageToken","nullable":true}},{"name":"ascending","in":"query","required":false,"schema":{"$ref":"#/components/schemas/Ascending","nullable":true}}],"responses":{"200":{"description":"","content":{"application/json":{"schema":{"$ref":"#/components/schemas/ListInboxEventsResponse"}}}},"404":{"description":"","content":{"application/json":{"schema":{"$ref":"#/components/schemas/ErrorResponse"}}}}},"summary":"List Inbox Events","servers":[{"url":"https://api.agentmail.to"},{"url":"https://x402.api.agentmail.to"},{"url":"https://mpp.api.agentmail.to"},{"url":"https://api.agentmail.eu"}],"security":[{"BearerAuth":[]},{"TokenAuth":[]}],"x-fern-sdk-group-name":["inboxes","events"],"x-fern-sdk-method-name":"list"}},"/v0/inboxes/{inbox_id}/lists/{direction}/{type}":{"get":{"description":"**CLI:**\n```bash\nagentmail inboxes lists list --inbox-id --direction --type \n```","operationId":"inboxes_lists_list","tags":["InboxesLists"],"parameters":[{"name":"inbox_id","in":"path","required":true,"schema":{"$ref":"#/components/schemas/inboxesInboxId"}},{"name":"direction","in":"path","required":true,"schema":{"$ref":"#/components/schemas/Direction"}},{"name":"type","in":"path","required":true,"schema":{"$ref":"#/components/schemas/ListType"}},{"name":"limit","in":"query","required":false,"schema":{"$ref":"#/components/schemas/Limit","nullable":true}},{"name":"page_token","in":"query","required":false,"schema":{"$ref":"#/components/schemas/PageToken","nullable":true}}],"responses":{"200":{"description":"","content":{"application/json":{"schema":{"$ref":"#/components/schemas/PodListListEntriesResponse"}}}}},"summary":"List Entries","servers":[{"url":"https://api.agentmail.to"},{"url":"https://x402.api.agentmail.to"},{"url":"https://mpp.api.agentmail.to"},{"url":"https://api.agentmail.eu"}],"security":[{"BearerAuth":[]},{"TokenAuth":[]}],"x-fern-sdk-group-name":["inboxes","lists"],"x-fern-sdk-method-name":"list"},"post":{"description":"**CLI:**\n```bash\nagentmail inboxes lists create --inbox-id --direction --type --entry user@example.com\n```","operationId":"inboxes_lists_create","tags":["InboxesLists"],"parameters":[{"name":"inbox_id","in":"path","required":true,"schema":{"$ref":"#/components/schemas/inboxesInboxId"}},{"name":"direction","in":"path","required":true,"schema":{"$ref":"#/components/schemas/Direction"}},{"name":"type","in":"path","required":true,"schema":{"$ref":"#/components/schemas/ListType"}}],"responses":{"200":{"description":"","content":{"application/json":{"schema":{"$ref":"#/components/schemas/PodListEntry"}}}},"400":{"description":"","content":{"application/json":{"schema":{"$ref":"#/components/schemas/ValidationErrorResponse"}}}}},"summary":"Create List Entry","servers":[{"url":"https://api.agentmail.to"},{"url":"https://x402.api.agentmail.to"},{"url":"https://mpp.api.agentmail.to"},{"url":"https://api.agentmail.eu"}],"security":[{"BearerAuth":[]},{"TokenAuth":[]}],"requestBody":{"required":true,"content":{"application/json":{"schema":{"$ref":"#/components/schemas/CreateListEntryRequest"}}}},"x-fern-sdk-group-name":["inboxes","lists"],"x-fern-sdk-method-name":"create"}},"/v0/inboxes/{inbox_id}/lists/{direction}/{type}/{entry}":{"get":{"description":"**CLI:**\n```bash\nagentmail inboxes lists get --inbox-id --direction --type --entry \n```","operationId":"inboxes_lists_get","tags":["InboxesLists"],"parameters":[{"name":"inbox_id","in":"path","required":true,"schema":{"$ref":"#/components/schemas/inboxesInboxId"}},{"name":"direction","in":"path","required":true,"schema":{"$ref":"#/components/schemas/Direction"}},{"name":"type","in":"path","required":true,"schema":{"$ref":"#/components/schemas/ListType"}},{"name":"entry","in":"path","description":"Email address or domain.","required":true,"schema":{"type":"string"}}],"responses":{"200":{"description":"","content":{"application/json":{"schema":{"$ref":"#/components/schemas/PodListEntry"}}}},"404":{"description":"","content":{"application/json":{"schema":{"$ref":"#/components/schemas/ErrorResponse"}}}}},"summary":"Get List Entry","servers":[{"url":"https://api.agentmail.to"},{"url":"https://x402.api.agentmail.to"},{"url":"https://mpp.api.agentmail.to"},{"url":"https://api.agentmail.eu"}],"security":[{"BearerAuth":[]},{"TokenAuth":[]}],"x-fern-sdk-group-name":["inboxes","lists"],"x-fern-sdk-method-name":"get"},"delete":{"description":"**CLI:**\n```bash\nagentmail inboxes lists delete --inbox-id --direction --type --entry \n```","operationId":"inboxes_lists_delete","tags":["InboxesLists"],"parameters":[{"name":"inbox_id","in":"path","required":true,"schema":{"$ref":"#/components/schemas/inboxesInboxId"}},{"name":"direction","in":"path","required":true,"schema":{"$ref":"#/components/schemas/Direction"}},{"name":"type","in":"path","required":true,"schema":{"$ref":"#/components/schemas/ListType"}},{"name":"entry","in":"path","description":"Email address or domain.","required":true,"schema":{"type":"string"}}],"responses":{"204":{"description":""},"404":{"description":"","content":{"application/json":{"schema":{"$ref":"#/components/schemas/ErrorResponse"}}}}},"summary":"Delete List Entry","servers":[{"url":"https://api.agentmail.to"},{"url":"https://x402.api.agentmail.to"},{"url":"https://mpp.api.agentmail.to"},{"url":"https://api.agentmail.eu"}],"security":[{"BearerAuth":[]},{"TokenAuth":[]}],"x-fern-sdk-group-name":["inboxes","lists"],"x-fern-sdk-method-name":"delete"}},"/v0/inboxes/{inbox_id}/messages":{"get":{"description":"Lists messages in the inbox, most recent first. Pass `from`, `to`, or\n`subject` to filter by substring. Filtered requests are served by\nsearch, which caps `limit` at 100. For relevance-ranked full-text\nsearch across sender, recipients, subject, and message body, use\n`Search Messages`.\n\n**CLI:**\n```bash\nagentmail inboxes messages list --inbox-id \n```","operationId":"inboxes_messages_list","tags":["InboxesMessages"],"parameters":[{"name":"inbox_id","in":"path","required":true,"schema":{"$ref":"#/components/schemas/inboxesInboxId"}},{"name":"limit","in":"query","required":false,"schema":{"$ref":"#/components/schemas/Limit","nullable":true}},{"name":"page_token","in":"query","required":false,"schema":{"$ref":"#/components/schemas/PageToken","nullable":true}},{"name":"labels","in":"query","required":false,"schema":{"$ref":"#/components/schemas/Labels","nullable":true}},{"name":"before","in":"query","required":false,"schema":{"$ref":"#/components/schemas/Before","nullable":true}},{"name":"after","in":"query","required":false,"schema":{"$ref":"#/components/schemas/After","nullable":true}},{"name":"ascending","in":"query","required":false,"schema":{"$ref":"#/components/schemas/Ascending","nullable":true}},{"name":"include_spam","in":"query","required":false,"schema":{"$ref":"#/components/schemas/IncludeSpam","nullable":true}},{"name":"include_blocked","in":"query","required":false,"schema":{"$ref":"#/components/schemas/IncludeBlocked","nullable":true}},{"name":"include_unauthenticated","in":"query","required":false,"schema":{"$ref":"#/components/schemas/IncludeUnauthenticated","nullable":true}},{"name":"include_trash","in":"query","required":false,"schema":{"$ref":"#/components/schemas/IncludeTrash","nullable":true}},{"name":"from","in":"query","description":"Filter to messages whose sender contains this value (substring match). Repeatable; all values must match.","required":false,"schema":{"type":"array","items":{"type":"string"},"nullable":true}},{"name":"to","in":"query","description":"Filter to messages whose recipients (to, cc, or bcc) contain this value (substring match). Repeatable; all values must match.","required":false,"schema":{"type":"array","items":{"type":"string"},"nullable":true}},{"name":"subject","in":"query","description":"Filter to messages whose subject contains this value (substring match). Repeatable; all values must match.","required":false,"schema":{"type":"array","items":{"type":"string"},"nullable":true}}],"responses":{"200":{"description":"","content":{"application/json":{"schema":{"$ref":"#/components/schemas/ListMessagesResponse"}}}},"404":{"description":"","content":{"application/json":{"schema":{"$ref":"#/components/schemas/ErrorResponse"}}}}},"summary":"List Messages","servers":[{"url":"https://api.agentmail.to"},{"url":"https://x402.api.agentmail.to"},{"url":"https://mpp.api.agentmail.to"},{"url":"https://api.agentmail.eu"}],"security":[{"BearerAuth":[]},{"TokenAuth":[]}],"x-fern-sdk-group-name":["inboxes","messages"],"x-fern-sdk-method-name":"list"}},"/v0/inboxes/{inbox_id}/messages/search":{"get":{"description":"Full-text search across messages in the inbox, ranked by relevance. The\nquery is matched against the sender, recipients, and subject (substring)\nand the message body (tokenized full text). Spam, trash, blocked, and\nunauthenticated messages are always excluded. `limit` cannot exceed 100.","operationId":"inboxes_messages_search","tags":["InboxesMessages"],"parameters":[{"name":"inbox_id","in":"path","required":true,"schema":{"$ref":"#/components/schemas/inboxesInboxId"}},{"name":"q","in":"query","required":true,"schema":{"$ref":"#/components/schemas/Query"}},{"name":"limit","in":"query","required":false,"schema":{"$ref":"#/components/schemas/Limit","nullable":true}},{"name":"page_token","in":"query","required":false,"schema":{"$ref":"#/components/schemas/PageToken","nullable":true}},{"name":"before","in":"query","required":false,"schema":{"$ref":"#/components/schemas/Before","nullable":true}},{"name":"after","in":"query","required":false,"schema":{"$ref":"#/components/schemas/After","nullable":true}}],"responses":{"200":{"description":"","content":{"application/json":{"schema":{"$ref":"#/components/schemas/SearchMessagesResponse"}}}},"400":{"description":"","content":{"application/json":{"schema":{"$ref":"#/components/schemas/ValidationErrorResponse"}}}},"404":{"description":"","content":{"application/json":{"schema":{"$ref":"#/components/schemas/ErrorResponse"}}}}},"summary":"Search Messages","servers":[{"url":"https://api.agentmail.to"},{"url":"https://x402.api.agentmail.to"},{"url":"https://mpp.api.agentmail.to"},{"url":"https://api.agentmail.eu"}],"security":[{"BearerAuth":[]},{"TokenAuth":[]}],"x-fern-sdk-group-name":["inboxes","messages"],"x-fern-sdk-method-name":"search"}},"/v0/inboxes/{inbox_id}/messages/{message_id}":{"get":{"description":"**CLI:**\n```bash\nagentmail inboxes messages get --inbox-id --message-id \n```","operationId":"inboxes_messages_get","tags":["InboxesMessages"],"parameters":[{"name":"inbox_id","in":"path","required":true,"schema":{"$ref":"#/components/schemas/inboxesInboxId"}},{"name":"message_id","in":"path","required":true,"schema":{"$ref":"#/components/schemas/MessageId"}}],"responses":{"200":{"description":"","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Message"}}}},"404":{"description":"","content":{"application/json":{"schema":{"$ref":"#/components/schemas/ErrorResponse"}}}}},"summary":"Get Message","servers":[{"url":"https://api.agentmail.to"},{"url":"https://x402.api.agentmail.to"},{"url":"https://mpp.api.agentmail.to"},{"url":"https://api.agentmail.eu"}],"security":[{"BearerAuth":[]},{"TokenAuth":[]}],"x-fern-sdk-group-name":["inboxes","messages"],"x-fern-sdk-method-name":"get"},"patch":{"description":"**CLI:**\n```bash\nagentmail inboxes messages update --inbox-id --message-id --add-labels read --remove-labels unread\n```","operationId":"inboxes_messages_update","tags":["InboxesMessages"],"parameters":[{"name":"inbox_id","in":"path","required":true,"schema":{"$ref":"#/components/schemas/inboxesInboxId"}},{"name":"message_id","in":"path","required":true,"schema":{"$ref":"#/components/schemas/MessageId"}}],"responses":{"200":{"description":"","content":{"application/json":{"schema":{"$ref":"#/components/schemas/UpdateMessageResponse"}}}},"400":{"description":"","content":{"application/json":{"schema":{"$ref":"#/components/schemas/ValidationErrorResponse"}}}},"404":{"description":"","content":{"application/json":{"schema":{"$ref":"#/components/schemas/ErrorResponse"}}}}},"summary":"Update Message","servers":[{"url":"https://api.agentmail.to"},{"url":"https://x402.api.agentmail.to"},{"url":"https://mpp.api.agentmail.to"},{"url":"https://api.agentmail.eu"}],"security":[{"BearerAuth":[]},{"TokenAuth":[]}],"requestBody":{"required":true,"content":{"application/json":{"schema":{"$ref":"#/components/schemas/UpdateMessageRequest"}}}},"x-fern-sdk-group-name":["inboxes","messages"],"x-fern-sdk-method-name":"update"},"delete":{"description":"Permanently deletes a message.\n\n**CLI:**\n```bash\nagentmail inboxes messages delete --inbox-id --message-id \n```","operationId":"inboxes_messages_delete","tags":["InboxesMessages"],"parameters":[{"name":"inbox_id","in":"path","required":true,"schema":{"$ref":"#/components/schemas/inboxesInboxId"}},{"name":"message_id","in":"path","required":true,"schema":{"$ref":"#/components/schemas/MessageId"}}],"responses":{"204":{"description":""},"404":{"description":"","content":{"application/json":{"schema":{"$ref":"#/components/schemas/ErrorResponse"}}}}},"summary":"Delete Message","servers":[{"url":"https://api.agentmail.to"},{"url":"https://x402.api.agentmail.to"},{"url":"https://mpp.api.agentmail.to"},{"url":"https://api.agentmail.eu"}],"security":[{"BearerAuth":[]},{"TokenAuth":[]}],"x-fern-sdk-group-name":["inboxes","messages"],"x-fern-sdk-method-name":"delete"}},"/v0/inboxes/{inbox_id}/messages/batch-get":{"post":{"description":"Fetch metadata for up to 500 messages in one request. Missing or\nrestricted IDs are silently omitted; compare `count` against `limit`\nto detect misses.\n\n**CLI:**\n```bash\nagentmail inboxes messages batch-get --inbox-id --message-ids --message-ids \n```","operationId":"inboxes_messages_batchGet","tags":["InboxesMessages"],"parameters":[{"name":"inbox_id","in":"path","required":true,"schema":{"$ref":"#/components/schemas/inboxesInboxId"}}],"responses":{"200":{"description":"","content":{"application/json":{"schema":{"$ref":"#/components/schemas/BatchGetMessagesResponse"}}}},"400":{"description":"","content":{"application/json":{"schema":{"$ref":"#/components/schemas/ValidationErrorResponse"}}}}},"summary":"Batch Get Messages","servers":[{"url":"https://api.agentmail.to"},{"url":"https://x402.api.agentmail.to"},{"url":"https://mpp.api.agentmail.to"},{"url":"https://api.agentmail.eu"}],"security":[{"BearerAuth":[]},{"TokenAuth":[]}],"requestBody":{"required":true,"content":{"application/json":{"schema":{"$ref":"#/components/schemas/BatchGetMessagesRequest"}}}},"x-fern-sdk-group-name":["inboxes","messages"],"x-fern-sdk-method-name":"batch-get"}},"/v0/inboxes/{inbox_id}/messages/batch-update":{"post":{"description":"Apply one label change to up to 50 messages in a single request. The\nsame add_labels and remove_labels apply to every message id, and at\nleast one of them must be provided. The update is atomic: either all\nresolved messages are updated or none are. Missing or restricted ids\nare silently excluded; compare `count` against `limit` to detect\nexclusions.\n\n**CLI:**\n```bash\nagentmail inboxes messages batch-update --inbox-id --message-ids --message-ids --add-labels read --remove-labels unread\n```","operationId":"inboxes_messages_batchUpdate","tags":["InboxesMessages"],"parameters":[{"name":"inbox_id","in":"path","required":true,"schema":{"$ref":"#/components/schemas/inboxesInboxId"}}],"responses":{"200":{"description":"","content":{"application/json":{"schema":{"$ref":"#/components/schemas/BatchUpdateMessagesResponse"}}}},"400":{"description":"","content":{"application/json":{"schema":{"$ref":"#/components/schemas/ValidationErrorResponse"}}}}},"summary":"Batch Update Messages","servers":[{"url":"https://api.agentmail.to"},{"url":"https://x402.api.agentmail.to"},{"url":"https://mpp.api.agentmail.to"},{"url":"https://api.agentmail.eu"}],"security":[{"BearerAuth":[]},{"TokenAuth":[]}],"requestBody":{"required":true,"content":{"application/json":{"schema":{"$ref":"#/components/schemas/BatchUpdateMessagesRequest"}}}},"x-fern-sdk-group-name":["inboxes","messages"],"x-fern-sdk-method-name":"batch-update"}},"/v0/inboxes/{inbox_id}/messages/{message_id}/attachments/{attachment_id}":{"get":{"description":"**CLI:**\n```bash\nagentmail inboxes messages get-attachment --inbox-id --message-id --attachment-id \n```","operationId":"inboxes_messages_getAttachment","tags":["InboxesMessages"],"parameters":[{"name":"inbox_id","in":"path","required":true,"schema":{"$ref":"#/components/schemas/inboxesInboxId"}},{"name":"message_id","in":"path","required":true,"schema":{"$ref":"#/components/schemas/MessageId"}},{"name":"attachment_id","in":"path","required":true,"schema":{"$ref":"#/components/schemas/AttachmentId"}}],"responses":{"200":{"description":"","content":{"application/json":{"schema":{"$ref":"#/components/schemas/AttachmentResponse"}}}},"404":{"description":"","content":{"application/json":{"schema":{"$ref":"#/components/schemas/ErrorResponse"}}}}},"summary":"Get Attachment","servers":[{"url":"https://api.agentmail.to"},{"url":"https://x402.api.agentmail.to"},{"url":"https://mpp.api.agentmail.to"},{"url":"https://api.agentmail.eu"}],"security":[{"BearerAuth":[]},{"TokenAuth":[]}],"x-fern-sdk-group-name":["inboxes","messages"],"x-fern-sdk-method-name":"get-attachment"}},"/v0/inboxes/{inbox_id}/messages/{message_id}/raw":{"get":{"description":"**CLI:**\n```bash\nagentmail inboxes messages get-raw --inbox-id --message-id \n```","operationId":"inboxes_messages_getRaw","tags":["InboxesMessages"],"parameters":[{"name":"inbox_id","in":"path","required":true,"schema":{"$ref":"#/components/schemas/inboxesInboxId"}},{"name":"message_id","in":"path","required":true,"schema":{"$ref":"#/components/schemas/MessageId"}}],"responses":{"200":{"description":"","content":{"application/json":{"schema":{"$ref":"#/components/schemas/RawMessageResponse"}}}},"404":{"description":"","content":{"application/json":{"schema":{"$ref":"#/components/schemas/ErrorResponse"}}}}},"summary":"Get Raw Message","servers":[{"url":"https://api.agentmail.to"},{"url":"https://x402.api.agentmail.to"},{"url":"https://mpp.api.agentmail.to"},{"url":"https://api.agentmail.eu"}],"security":[{"BearerAuth":[]},{"TokenAuth":[]}],"x-fern-sdk-group-name":["inboxes","messages"],"x-fern-sdk-method-name":"get-raw"}},"/v0/inboxes/{inbox_id}/messages/send":{"post":{"description":"**CLI:**\n```bash\nagentmail inboxes messages send --inbox-id --to recipient@example.com --subject \"Hello\" --text \"Body\"\n```","operationId":"inboxes_messages_send","tags":["InboxesMessages"],"parameters":[{"name":"inbox_id","in":"path","required":true,"schema":{"$ref":"#/components/schemas/inboxesInboxId"}},{"name":"Idempotency-Key","in":"header","required":false,"description":"Unique key that makes a send idempotent. A retry carrying the same key returns the original message instead of sending a second email; reusing a key with a different request returns a 409 conflict. Keys expire 24 hours after the send completes.","schema":{"type":"string"}}],"responses":{"200":{"description":"","content":{"application/json":{"schema":{"$ref":"#/components/schemas/SendMessageResponse"}}}},"400":{"description":"","content":{"application/json":{"schema":{"$ref":"#/components/schemas/ValidationErrorResponse"}}}},"403":{"description":"","content":{"application/json":{"schema":{"$ref":"#/components/schemas/ErrorResponse"}}}},"404":{"description":"","content":{"application/json":{"schema":{"$ref":"#/components/schemas/ErrorResponse"}}}},"409":{"description":"","content":{"application/json":{"schema":{"$ref":"#/components/schemas/ErrorResponse"}}}}},"summary":"Send Message","servers":[{"url":"https://api.agentmail.to"},{"url":"https://x402.api.agentmail.to"},{"url":"https://mpp.api.agentmail.to"},{"url":"https://api.agentmail.eu"}],"security":[{"BearerAuth":[]},{"TokenAuth":[]}],"requestBody":{"required":true,"content":{"application/json":{"schema":{"$ref":"#/components/schemas/SendMessageRequest"}}}},"x-fern-sdk-group-name":["inboxes","messages"],"x-fern-sdk-method-name":"send"}},"/v0/inboxes/{inbox_id}/messages/{message_id}/reply":{"post":{"description":"**CLI:**\n```bash\nagentmail inboxes messages reply --inbox-id --message-id --text \"Reply text\"\n```","operationId":"inboxes_messages_reply","tags":["InboxesMessages"],"parameters":[{"name":"inbox_id","in":"path","required":true,"schema":{"$ref":"#/components/schemas/inboxesInboxId"}},{"name":"message_id","in":"path","required":true,"schema":{"$ref":"#/components/schemas/MessageId"}},{"name":"Idempotency-Key","in":"header","required":false,"description":"Unique key that makes a send idempotent. A retry carrying the same key returns the original message instead of sending a second email; reusing a key with a different request returns a 409 conflict. Keys expire 24 hours after the send completes.","schema":{"type":"string"}}],"responses":{"200":{"description":"","content":{"application/json":{"schema":{"$ref":"#/components/schemas/SendMessageResponse"}}}},"400":{"description":"","content":{"application/json":{"schema":{"$ref":"#/components/schemas/ValidationErrorResponse"}}}},"403":{"description":"","content":{"application/json":{"schema":{"$ref":"#/components/schemas/ErrorResponse"}}}},"404":{"description":"","content":{"application/json":{"schema":{"$ref":"#/components/schemas/ErrorResponse"}}}},"409":{"description":"","content":{"application/json":{"schema":{"$ref":"#/components/schemas/ErrorResponse"}}}}},"summary":"Reply To Message","servers":[{"url":"https://api.agentmail.to"},{"url":"https://x402.api.agentmail.to"},{"url":"https://mpp.api.agentmail.to"},{"url":"https://api.agentmail.eu"}],"security":[{"BearerAuth":[]},{"TokenAuth":[]}],"requestBody":{"required":true,"content":{"application/json":{"schema":{"$ref":"#/components/schemas/ReplyToMessageRequest"}}}},"x-fern-sdk-group-name":["inboxes","messages"],"x-fern-sdk-method-name":"reply"}},"/v0/inboxes/{inbox_id}/messages/{message_id}/reply-all":{"post":{"description":"**CLI:**\n```bash\nagentmail inboxes messages reply-all --inbox-id --message-id --text \"Reply text\"\n```","operationId":"inboxes_messages_reply-all","tags":["InboxesMessages"],"parameters":[{"name":"inbox_id","in":"path","required":true,"schema":{"$ref":"#/components/schemas/inboxesInboxId"}},{"name":"message_id","in":"path","required":true,"schema":{"$ref":"#/components/schemas/MessageId"}},{"name":"Idempotency-Key","in":"header","required":false,"description":"Unique key that makes a send idempotent. A retry carrying the same key returns the original message instead of sending a second email; reusing a key with a different request returns a 409 conflict. Keys expire 24 hours after the send completes.","schema":{"type":"string"}}],"responses":{"200":{"description":"","content":{"application/json":{"schema":{"$ref":"#/components/schemas/SendMessageResponse"}}}},"400":{"description":"","content":{"application/json":{"schema":{"$ref":"#/components/schemas/ValidationErrorResponse"}}}},"403":{"description":"","content":{"application/json":{"schema":{"$ref":"#/components/schemas/ErrorResponse"}}}},"404":{"description":"","content":{"application/json":{"schema":{"$ref":"#/components/schemas/ErrorResponse"}}}},"409":{"description":"","content":{"application/json":{"schema":{"$ref":"#/components/schemas/ErrorResponse"}}}}},"summary":"Reply All Message","servers":[{"url":"https://api.agentmail.to"},{"url":"https://x402.api.agentmail.to"},{"url":"https://mpp.api.agentmail.to"},{"url":"https://api.agentmail.eu"}],"security":[{"BearerAuth":[]},{"TokenAuth":[]}],"requestBody":{"required":true,"content":{"application/json":{"schema":{"$ref":"#/components/schemas/ReplyAllMessageRequest"}}}},"x-fern-sdk-group-name":["inboxes","messages"],"x-fern-sdk-method-name":"reply-all"}},"/v0/inboxes/{inbox_id}/messages/{message_id}/forward":{"post":{"description":"**CLI:**\n```bash\nagentmail inboxes messages forward --inbox-id --message-id --to recipient@example.com\n```","operationId":"inboxes_messages_forward","tags":["InboxesMessages"],"parameters":[{"name":"inbox_id","in":"path","required":true,"schema":{"$ref":"#/components/schemas/inboxesInboxId"}},{"name":"message_id","in":"path","required":true,"schema":{"$ref":"#/components/schemas/MessageId"}},{"name":"Idempotency-Key","in":"header","required":false,"description":"Unique key that makes a send idempotent. A retry carrying the same key returns the original message instead of sending a second email; reusing a key with a different request returns a 409 conflict. Keys expire 24 hours after the send completes.","schema":{"type":"string"}}],"responses":{"200":{"description":"","content":{"application/json":{"schema":{"$ref":"#/components/schemas/SendMessageResponse"}}}},"400":{"description":"","content":{"application/json":{"schema":{"$ref":"#/components/schemas/ValidationErrorResponse"}}}},"403":{"description":"","content":{"application/json":{"schema":{"$ref":"#/components/schemas/ErrorResponse"}}}},"404":{"description":"","content":{"application/json":{"schema":{"$ref":"#/components/schemas/ErrorResponse"}}}},"409":{"description":"","content":{"application/json":{"schema":{"$ref":"#/components/schemas/ErrorResponse"}}}}},"summary":"Forward Message","servers":[{"url":"https://api.agentmail.to"},{"url":"https://x402.api.agentmail.to"},{"url":"https://mpp.api.agentmail.to"},{"url":"https://api.agentmail.eu"}],"security":[{"BearerAuth":[]},{"TokenAuth":[]}],"requestBody":{"required":true,"content":{"application/json":{"schema":{"$ref":"#/components/schemas/SendMessageRequest"}}}},"x-fern-sdk-group-name":["inboxes","messages"],"x-fern-sdk-method-name":"forward"}},"/v0/inboxes/{inbox_id}/metrics/events":{"get":{"description":"Counts of email events (sent, delivered, bounced, etc.) over time for\nthe inbox. Defaults to the last 24 hours; `start` must be within the\nlast 90 days, and a future `end` is clamped to now. Omit `period` for\nindividual event counts, or set it to sum counts into buckets of that\nmany seconds.\n\n**CLI:**\n```bash\nagentmail inboxes metrics query-events --inbox-id \n```","operationId":"inboxes_metrics_queryEvents","tags":["InboxesMetrics"],"parameters":[{"name":"inbox_id","in":"path","required":true,"schema":{"$ref":"#/components/schemas/inboxesInboxId"}},{"name":"event_types","in":"query","required":false,"schema":{"$ref":"#/components/schemas/MetricEventTypes","nullable":true}},{"name":"start","in":"query","required":false,"schema":{"$ref":"#/components/schemas/Start","nullable":true}},{"name":"end","in":"query","required":false,"schema":{"$ref":"#/components/schemas/End","nullable":true}},{"name":"period","in":"query","required":false,"schema":{"$ref":"#/components/schemas/Period","nullable":true}},{"name":"limit","in":"query","required":false,"schema":{"$ref":"#/components/schemas/MetricLimit","nullable":true}},{"name":"descending","in":"query","required":false,"schema":{"$ref":"#/components/schemas/Descending","nullable":true}}],"responses":{"200":{"description":"","content":{"application/json":{"schema":{"$ref":"#/components/schemas/QueryMetricsResponse"}}}},"400":{"description":"","content":{"application/json":{"schema":{"$ref":"#/components/schemas/ValidationErrorResponse"}}}}},"summary":"Query Events","servers":[{"url":"https://api.agentmail.to"},{"url":"https://x402.api.agentmail.to"},{"url":"https://mpp.api.agentmail.to"},{"url":"https://api.agentmail.eu"}],"security":[{"BearerAuth":[]},{"TokenAuth":[]}],"x-fern-sdk-group-name":["inboxes","metrics"],"x-fern-sdk-method-name":"query-events"}},"/v0/inboxes/{inbox_id}/metrics/usage":{"get":{"description":"Cumulative usage series for the inbox. Each point is the running total\nof the usage type at that timestamp, not the change within the bucket.\nInbox-scoped queries carry `storage_bytes`, `message_count`, and\n`thread_count`; requested types that don't apply to the scope are\nignored. Defaults to the last 24 hours; `start` must be within the\nlast 90 days, and a future `end` is clamped to now. The range divided\nby `period` must not exceed 1000 buckets.","operationId":"inboxes_metrics_queryUsage","tags":["InboxesMetrics"],"parameters":[{"name":"inbox_id","in":"path","required":true,"schema":{"$ref":"#/components/schemas/inboxesInboxId"}},{"name":"usage_types","in":"query","required":false,"schema":{"$ref":"#/components/schemas/UsageTypes","nullable":true}},{"name":"start","in":"query","required":false,"schema":{"$ref":"#/components/schemas/Start","nullable":true}},{"name":"end","in":"query","required":false,"schema":{"$ref":"#/components/schemas/End","nullable":true}},{"name":"period","in":"query","required":false,"schema":{"$ref":"#/components/schemas/Period","nullable":true}},{"name":"limit","in":"query","required":false,"schema":{"$ref":"#/components/schemas/MetricLimit","nullable":true}},{"name":"descending","in":"query","required":false,"schema":{"$ref":"#/components/schemas/Descending","nullable":true}}],"responses":{"200":{"description":"","content":{"application/json":{"schema":{"$ref":"#/components/schemas/QueryUsageResponse"}}}},"400":{"description":"","content":{"application/json":{"schema":{"$ref":"#/components/schemas/ValidationErrorResponse"}}}}},"summary":"Query Usage","servers":[{"url":"https://api.agentmail.to"},{"url":"https://x402.api.agentmail.to"},{"url":"https://mpp.api.agentmail.to"},{"url":"https://api.agentmail.eu"}],"security":[{"BearerAuth":[]},{"TokenAuth":[]}],"x-fern-sdk-group-name":["inboxes","metrics"],"x-fern-sdk-method-name":"query-usage"}},"/v0/inboxes/{inbox_id}/threads":{"get":{"description":"Lists threads in the inbox, most recent first. Pass `senders`,\n`recipients`, or `subject` to filter by substring. Filtered requests are\nserved by search, which caps `limit` at 100. For relevance-ranked\nfull-text search, use `Search Threads`.\n\n**CLI:**\n```bash\nagentmail inboxes threads list --inbox-id \n```","operationId":"inboxes_threads_list","tags":["InboxesThreads"],"parameters":[{"name":"inbox_id","in":"path","required":true,"schema":{"$ref":"#/components/schemas/inboxesInboxId"}},{"name":"limit","in":"query","required":false,"schema":{"$ref":"#/components/schemas/Limit","nullable":true}},{"name":"page_token","in":"query","required":false,"schema":{"$ref":"#/components/schemas/PageToken","nullable":true}},{"name":"labels","in":"query","required":false,"schema":{"$ref":"#/components/schemas/Labels","nullable":true}},{"name":"before","in":"query","required":false,"schema":{"$ref":"#/components/schemas/Before","nullable":true}},{"name":"after","in":"query","required":false,"schema":{"$ref":"#/components/schemas/After","nullable":true}},{"name":"ascending","in":"query","required":false,"schema":{"$ref":"#/components/schemas/Ascending","nullable":true}},{"name":"include_spam","in":"query","required":false,"schema":{"$ref":"#/components/schemas/IncludeSpam","nullable":true}},{"name":"include_blocked","in":"query","required":false,"schema":{"$ref":"#/components/schemas/IncludeBlocked","nullable":true}},{"name":"include_unauthenticated","in":"query","required":false,"schema":{"$ref":"#/components/schemas/IncludeUnauthenticated","nullable":true}},{"name":"include_trash","in":"query","required":false,"schema":{"$ref":"#/components/schemas/IncludeTrash","nullable":true}},{"name":"senders","in":"query","description":"Filter to threads whose senders contain this value (substring match). Repeatable; all values must match.","required":false,"schema":{"type":"array","items":{"type":"string"},"nullable":true}},{"name":"recipients","in":"query","description":"Filter to threads whose recipients contain this value (substring match). Repeatable; all values must match.","required":false,"schema":{"type":"array","items":{"type":"string"},"nullable":true}},{"name":"subject","in":"query","description":"Filter to threads whose subject contains this value (substring match). Repeatable; all values must match.","required":false,"schema":{"type":"array","items":{"type":"string"},"nullable":true}}],"responses":{"200":{"description":"","content":{"application/json":{"schema":{"$ref":"#/components/schemas/ListThreadsResponse"}}}},"404":{"description":"","content":{"application/json":{"schema":{"$ref":"#/components/schemas/ErrorResponse"}}}}},"summary":"List Threads","servers":[{"url":"https://api.agentmail.to"},{"url":"https://x402.api.agentmail.to"},{"url":"https://mpp.api.agentmail.to"},{"url":"https://api.agentmail.eu"}],"security":[{"BearerAuth":[]},{"TokenAuth":[]}],"x-fern-sdk-group-name":["inboxes","threads"],"x-fern-sdk-method-name":"list"}},"/v0/inboxes/{inbox_id}/threads/search":{"get":{"description":"Full-text search across threads in the inbox, ranked by relevance. The\nquery is matched against senders, recipients, and subject (substring)\nand the message body (tokenized full text). Spam, trash, blocked, and\nunauthenticated threads are always excluded. `limit` cannot exceed 100.","operationId":"inboxes_threads_search","tags":["InboxesThreads"],"parameters":[{"name":"inbox_id","in":"path","required":true,"schema":{"$ref":"#/components/schemas/inboxesInboxId"}},{"name":"q","in":"query","required":true,"schema":{"$ref":"#/components/schemas/Query"}},{"name":"limit","in":"query","required":false,"schema":{"$ref":"#/components/schemas/Limit","nullable":true}},{"name":"page_token","in":"query","required":false,"schema":{"$ref":"#/components/schemas/PageToken","nullable":true}},{"name":"before","in":"query","required":false,"schema":{"$ref":"#/components/schemas/Before","nullable":true}},{"name":"after","in":"query","required":false,"schema":{"$ref":"#/components/schemas/After","nullable":true}}],"responses":{"200":{"description":"","content":{"application/json":{"schema":{"$ref":"#/components/schemas/SearchThreadsResponse"}}}},"400":{"description":"","content":{"application/json":{"schema":{"$ref":"#/components/schemas/ValidationErrorResponse"}}}},"404":{"description":"","content":{"application/json":{"schema":{"$ref":"#/components/schemas/ErrorResponse"}}}}},"summary":"Search Threads","servers":[{"url":"https://api.agentmail.to"},{"url":"https://x402.api.agentmail.to"},{"url":"https://mpp.api.agentmail.to"},{"url":"https://api.agentmail.eu"}],"security":[{"BearerAuth":[]},{"TokenAuth":[]}],"x-fern-sdk-group-name":["inboxes","threads"],"x-fern-sdk-method-name":"search"}},"/v0/inboxes/{inbox_id}/threads/{thread_id}":{"get":{"description":"**CLI:**\n```bash\nagentmail inboxes threads get --inbox-id --thread-id \n```","operationId":"inboxes_threads_get","tags":["InboxesThreads"],"parameters":[{"name":"inbox_id","in":"path","required":true,"schema":{"$ref":"#/components/schemas/inboxesInboxId"}},{"name":"thread_id","in":"path","required":true,"schema":{"$ref":"#/components/schemas/ThreadId"}}],"responses":{"200":{"description":"","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Thread"}}}},"404":{"description":"","content":{"application/json":{"schema":{"$ref":"#/components/schemas/ErrorResponse"}}}}},"summary":"Get Thread","servers":[{"url":"https://api.agentmail.to"},{"url":"https://x402.api.agentmail.to"},{"url":"https://mpp.api.agentmail.to"},{"url":"https://api.agentmail.eu"}],"security":[{"BearerAuth":[]},{"TokenAuth":[]}],"x-fern-sdk-group-name":["inboxes","threads"],"x-fern-sdk-method-name":"get"},"patch":{"description":"Updates thread labels. Cannot add or remove system labels (sent, received, bounced, etc.). Rejects requests with a `422` for threads with 100 or more messages.","operationId":"inboxes_threads_update","tags":["InboxesThreads"],"parameters":[{"name":"inbox_id","in":"path","required":true,"schema":{"$ref":"#/components/schemas/inboxesInboxId"}},{"name":"thread_id","in":"path","required":true,"schema":{"$ref":"#/components/schemas/ThreadId"}}],"responses":{"200":{"description":"","content":{"application/json":{"schema":{"$ref":"#/components/schemas/UpdateThreadResponse"}}}},"400":{"description":"","content":{"application/json":{"schema":{"$ref":"#/components/schemas/ValidationErrorResponse"}}}},"404":{"description":"","content":{"application/json":{"schema":{"$ref":"#/components/schemas/ErrorResponse"}}}},"422":{"description":"","content":{"application/json":{"schema":{"$ref":"#/components/schemas/ErrorResponse"}}}}},"summary":"Update Thread","servers":[{"url":"https://api.agentmail.to"},{"url":"https://x402.api.agentmail.to"},{"url":"https://mpp.api.agentmail.to"},{"url":"https://api.agentmail.eu"}],"security":[{"BearerAuth":[]},{"TokenAuth":[]}],"requestBody":{"required":true,"content":{"application/json":{"schema":{"$ref":"#/components/schemas/UpdateThreadRequest"}}}},"x-fern-sdk-group-name":["inboxes","threads"],"x-fern-sdk-method-name":"update"},"delete":{"description":"Permanently deletes a thread and all of its messages.\n\n**CLI:**\n```bash\nagentmail inboxes threads delete --inbox-id --thread-id \n```","operationId":"inboxes_threads_delete","tags":["InboxesThreads"],"parameters":[{"name":"inbox_id","in":"path","required":true,"schema":{"$ref":"#/components/schemas/inboxesInboxId"}},{"name":"thread_id","in":"path","required":true,"schema":{"$ref":"#/components/schemas/ThreadId"}}],"responses":{"204":{"description":""},"404":{"description":"","content":{"application/json":{"schema":{"$ref":"#/components/schemas/ErrorResponse"}}}}},"summary":"Delete Thread","servers":[{"url":"https://api.agentmail.to"},{"url":"https://x402.api.agentmail.to"},{"url":"https://mpp.api.agentmail.to"},{"url":"https://api.agentmail.eu"}],"security":[{"BearerAuth":[]},{"TokenAuth":[]}],"x-fern-sdk-group-name":["inboxes","threads"],"x-fern-sdk-method-name":"delete"}},"/v0/inboxes/{inbox_id}/threads/{thread_id}/attachments/{attachment_id}":{"get":{"description":"**CLI:**\n```bash\nagentmail inboxes threads get-attachment --inbox-id --thread-id --attachment-id \n```","operationId":"inboxes_threads_getAttachment","tags":["InboxesThreads"],"parameters":[{"name":"inbox_id","in":"path","required":true,"schema":{"$ref":"#/components/schemas/inboxesInboxId"}},{"name":"thread_id","in":"path","required":true,"schema":{"$ref":"#/components/schemas/ThreadId"}},{"name":"attachment_id","in":"path","required":true,"schema":{"$ref":"#/components/schemas/AttachmentId"}}],"responses":{"200":{"description":"","content":{"application/json":{"schema":{"$ref":"#/components/schemas/AttachmentResponse"}}}},"404":{"description":"","content":{"application/json":{"schema":{"$ref":"#/components/schemas/ErrorResponse"}}}}},"summary":"Get Attachment","servers":[{"url":"https://api.agentmail.to"},{"url":"https://x402.api.agentmail.to"},{"url":"https://mpp.api.agentmail.to"},{"url":"https://api.agentmail.eu"}],"security":[{"BearerAuth":[]},{"TokenAuth":[]}],"x-fern-sdk-group-name":["inboxes","threads"],"x-fern-sdk-method-name":"get-attachment"}},"/v0/inboxes/{inbox_id}/webhooks":{"get":{"description":"**CLI:**\n```bash\nagentmail inboxes webhooks list --inbox-id \n```","operationId":"inboxes_webhooks_list","tags":["InboxesWebhooks"],"parameters":[{"name":"inbox_id","in":"path","required":true,"schema":{"$ref":"#/components/schemas/inboxesInboxId"}},{"name":"limit","in":"query","required":false,"schema":{"$ref":"#/components/schemas/Limit","nullable":true}},{"name":"page_token","in":"query","required":false,"schema":{"$ref":"#/components/schemas/PageToken","nullable":true}},{"name":"ascending","in":"query","required":false,"schema":{"$ref":"#/components/schemas/Ascending","nullable":true}}],"responses":{"200":{"description":"","content":{"application/json":{"schema":{"$ref":"#/components/schemas/webhooksListWebhooksResponse"}}}}},"summary":"List Webhooks","servers":[{"url":"https://api.agentmail.to"},{"url":"https://x402.api.agentmail.to"},{"url":"https://mpp.api.agentmail.to"},{"url":"https://api.agentmail.eu"}],"security":[{"BearerAuth":[]},{"TokenAuth":[]}],"x-fern-sdk-group-name":["inboxes","webhooks"],"x-fern-sdk-method-name":"list"},"post":{"description":"Create a webhook scoped to this inbox.\n\n**CLI:**\n```bash\nagentmail inboxes webhooks create --inbox-id --url https://example.com/webhook --event-types message.received\n```","operationId":"inboxes_webhooks_create","tags":["InboxesWebhooks"],"parameters":[{"name":"inbox_id","in":"path","required":true,"schema":{"$ref":"#/components/schemas/inboxesInboxId"}}],"responses":{"200":{"description":"","content":{"application/json":{"schema":{"$ref":"#/components/schemas/webhooksWebhook"}}}},"400":{"description":"","content":{"application/json":{"schema":{"$ref":"#/components/schemas/ValidationErrorResponse"}}}}},"summary":"Create Webhook","servers":[{"url":"https://api.agentmail.to"},{"url":"https://x402.api.agentmail.to"},{"url":"https://mpp.api.agentmail.to"},{"url":"https://api.agentmail.eu"}],"security":[{"BearerAuth":[]},{"TokenAuth":[]}],"requestBody":{"required":true,"content":{"application/json":{"schema":{"$ref":"#/components/schemas/webhooksCreateInboxWebhookRequest"}}}},"x-fern-sdk-group-name":["inboxes","webhooks"],"x-fern-sdk-method-name":"create"}},"/v0/inboxes/{inbox_id}/webhooks/{webhook_id}":{"get":{"description":"**CLI:**\n```bash\nagentmail inboxes webhooks get --inbox-id --webhook-id \n```","operationId":"inboxes_webhooks_get","tags":["InboxesWebhooks"],"parameters":[{"name":"inbox_id","in":"path","required":true,"schema":{"$ref":"#/components/schemas/inboxesInboxId"}},{"name":"webhook_id","in":"path","required":true,"schema":{"$ref":"#/components/schemas/webhooksWebhookId"}}],"responses":{"200":{"description":"","content":{"application/json":{"schema":{"$ref":"#/components/schemas/webhooksWebhook"}}}},"404":{"description":"","content":{"application/json":{"schema":{"$ref":"#/components/schemas/ErrorResponse"}}}}},"summary":"Get Webhook","servers":[{"url":"https://api.agentmail.to"},{"url":"https://x402.api.agentmail.to"},{"url":"https://mpp.api.agentmail.to"},{"url":"https://api.agentmail.eu"}],"security":[{"BearerAuth":[]},{"TokenAuth":[]}],"x-fern-sdk-group-name":["inboxes","webhooks"],"x-fern-sdk-method-name":"get"},"patch":{"description":"**CLI:**\n```bash\nagentmail inboxes webhooks update --inbox-id --webhook-id --event-types message.received\n```","operationId":"inboxes_webhooks_update","tags":["InboxesWebhooks"],"parameters":[{"name":"inbox_id","in":"path","required":true,"schema":{"$ref":"#/components/schemas/inboxesInboxId"}},{"name":"webhook_id","in":"path","required":true,"schema":{"$ref":"#/components/schemas/webhooksWebhookId"}}],"responses":{"200":{"description":"","content":{"application/json":{"schema":{"$ref":"#/components/schemas/webhooksWebhook"}}}},"400":{"description":"","content":{"application/json":{"schema":{"$ref":"#/components/schemas/ValidationErrorResponse"}}}},"404":{"description":"","content":{"application/json":{"schema":{"$ref":"#/components/schemas/ErrorResponse"}}}}},"summary":"Update Webhook","servers":[{"url":"https://api.agentmail.to"},{"url":"https://x402.api.agentmail.to"},{"url":"https://mpp.api.agentmail.to"},{"url":"https://api.agentmail.eu"}],"security":[{"BearerAuth":[]},{"TokenAuth":[]}],"requestBody":{"required":true,"content":{"application/json":{"schema":{"$ref":"#/components/schemas/webhooksUpdateInboxWebhookRequest"}}}},"x-fern-sdk-group-name":["inboxes","webhooks"],"x-fern-sdk-method-name":"update"},"delete":{"description":"**CLI:**\n```bash\nagentmail inboxes webhooks delete --inbox-id --webhook-id \n```","operationId":"inboxes_webhooks_delete","tags":["InboxesWebhooks"],"parameters":[{"name":"inbox_id","in":"path","required":true,"schema":{"$ref":"#/components/schemas/inboxesInboxId"}},{"name":"webhook_id","in":"path","required":true,"schema":{"$ref":"#/components/schemas/webhooksWebhookId"}}],"responses":{"204":{"description":""},"404":{"description":"","content":{"application/json":{"schema":{"$ref":"#/components/schemas/ErrorResponse"}}}}},"summary":"Delete Webhook","servers":[{"url":"https://api.agentmail.to"},{"url":"https://x402.api.agentmail.to"},{"url":"https://mpp.api.agentmail.to"},{"url":"https://api.agentmail.eu"}],"security":[{"BearerAuth":[]},{"TokenAuth":[]}],"x-fern-sdk-group-name":["inboxes","webhooks"],"x-fern-sdk-method-name":"delete"}},"/v0/inboxes/{inbox_id}/webhooks/{webhook_id}/headers":{"get":{"description":"List the names of custom HTTP headers included with deliveries to this inbox-scoped webhook.\nHeader values are write-only and are never returned.","operationId":"inboxes_webhooks_getHeaders","tags":["InboxesWebhooks"],"parameters":[{"name":"inbox_id","in":"path","required":true,"schema":{"$ref":"#/components/schemas/inboxesInboxId"}},{"name":"webhook_id","in":"path","required":true,"schema":{"$ref":"#/components/schemas/webhooksWebhookId"}}],"responses":{"200":{"description":"","content":{"application/json":{"schema":{"$ref":"#/components/schemas/webhooksWebhookHeaderNamesResponse"}}}},"404":{"description":"","content":{"application/json":{"schema":{"$ref":"#/components/schemas/ErrorResponse"}}}}},"summary":"Get Webhook Headers","servers":[{"url":"https://api.agentmail.to"},{"url":"https://x402.api.agentmail.to"},{"url":"https://mpp.api.agentmail.to"},{"url":"https://api.agentmail.eu"}],"security":[{"BearerAuth":[]},{"TokenAuth":[]}],"x-fern-sdk-group-name":["inboxes","webhooks"],"x-fern-sdk-method-name":"get-headers"},"patch":{"description":"Atomically set, replace, or remove custom HTTP headers included with deliveries to this\ninbox-scoped webhook. Header values remain write-only.","operationId":"inboxes_webhooks_updateHeaders","tags":["InboxesWebhooks"],"parameters":[{"name":"inbox_id","in":"path","required":true,"schema":{"$ref":"#/components/schemas/inboxesInboxId"}},{"name":"webhook_id","in":"path","required":true,"schema":{"$ref":"#/components/schemas/webhooksWebhookId"}}],"responses":{"204":{"description":""},"400":{"description":"","content":{"application/json":{"schema":{"$ref":"#/components/schemas/ValidationErrorResponse"}}}},"404":{"description":"","content":{"application/json":{"schema":{"$ref":"#/components/schemas/ErrorResponse"}}}}},"summary":"Update Webhook Headers","servers":[{"url":"https://api.agentmail.to"},{"url":"https://x402.api.agentmail.to"},{"url":"https://mpp.api.agentmail.to"},{"url":"https://api.agentmail.eu"}],"security":[{"BearerAuth":[]},{"TokenAuth":[]}],"requestBody":{"required":true,"content":{"application/json":{"schema":{"$ref":"#/components/schemas/webhooksUpdateWebhookHeadersRequest"}}}},"x-fern-sdk-group-name":["inboxes","webhooks"],"x-fern-sdk-method-name":"update-headers"}},"/v0/lists/{direction}/{type}":{"get":{"description":"**CLI:**\n```bash\nagentmail lists list --direction --type \n```","operationId":"lists_list","tags":["Lists"],"parameters":[{"name":"direction","in":"path","required":true,"schema":{"$ref":"#/components/schemas/Direction"}},{"name":"type","in":"path","required":true,"schema":{"$ref":"#/components/schemas/ListType"}},{"name":"limit","in":"query","required":false,"schema":{"$ref":"#/components/schemas/Limit","nullable":true}},{"name":"page_token","in":"query","required":false,"schema":{"$ref":"#/components/schemas/PageToken","nullable":true}}],"responses":{"200":{"description":"","content":{"application/json":{"schema":{"$ref":"#/components/schemas/ListListEntriesResponse"}}}}},"summary":"List Entries","servers":[{"url":"https://api.agentmail.to"},{"url":"https://x402.api.agentmail.to"},{"url":"https://mpp.api.agentmail.to"},{"url":"https://api.agentmail.eu"}],"security":[{"BearerAuth":[]},{"TokenAuth":[]}],"x-fern-sdk-group-name":["lists"],"x-fern-sdk-method-name":"list"},"post":{"description":"**CLI:**\n```bash\nagentmail lists create --direction --type --entry user@example.com\n```","operationId":"lists_create","tags":["Lists"],"parameters":[{"name":"direction","in":"path","required":true,"schema":{"$ref":"#/components/schemas/Direction"}},{"name":"type","in":"path","required":true,"schema":{"$ref":"#/components/schemas/ListType"}}],"responses":{"200":{"description":"","content":{"application/json":{"schema":{"$ref":"#/components/schemas/ListEntry"}}}},"400":{"description":"","content":{"application/json":{"schema":{"$ref":"#/components/schemas/ValidationErrorResponse"}}}}},"summary":"Create List Entry","servers":[{"url":"https://api.agentmail.to"},{"url":"https://x402.api.agentmail.to"},{"url":"https://mpp.api.agentmail.to"},{"url":"https://api.agentmail.eu"}],"security":[{"BearerAuth":[]},{"TokenAuth":[]}],"requestBody":{"required":true,"content":{"application/json":{"schema":{"$ref":"#/components/schemas/CreateListEntryRequest"}}}},"x-fern-sdk-group-name":["lists"],"x-fern-sdk-method-name":"create"}},"/v0/lists/{direction}/{type}/{entry}":{"get":{"description":"**CLI:**\n```bash\nagentmail lists get --direction --type --entry \n```","operationId":"lists_get","tags":["Lists"],"parameters":[{"name":"direction","in":"path","required":true,"schema":{"$ref":"#/components/schemas/Direction"}},{"name":"type","in":"path","required":true,"schema":{"$ref":"#/components/schemas/ListType"}},{"name":"entry","in":"path","description":"Email address or domain.","required":true,"schema":{"type":"string"}}],"responses":{"200":{"description":"","content":{"application/json":{"schema":{"$ref":"#/components/schemas/ListEntry"}}}},"404":{"description":"","content":{"application/json":{"schema":{"$ref":"#/components/schemas/ErrorResponse"}}}}},"summary":"Get List Entry","servers":[{"url":"https://api.agentmail.to"},{"url":"https://x402.api.agentmail.to"},{"url":"https://mpp.api.agentmail.to"},{"url":"https://api.agentmail.eu"}],"security":[{"BearerAuth":[]},{"TokenAuth":[]}],"x-fern-sdk-group-name":["lists"],"x-fern-sdk-method-name":"get"},"delete":{"description":"**CLI:**\n```bash\nagentmail lists delete --direction --type --entry \n```","operationId":"lists_delete","tags":["Lists"],"parameters":[{"name":"direction","in":"path","required":true,"schema":{"$ref":"#/components/schemas/Direction"}},{"name":"type","in":"path","required":true,"schema":{"$ref":"#/components/schemas/ListType"}},{"name":"entry","in":"path","description":"Email address or domain.","required":true,"schema":{"type":"string"}}],"responses":{"204":{"description":""},"404":{"description":"","content":{"application/json":{"schema":{"$ref":"#/components/schemas/ErrorResponse"}}}}},"summary":"Delete List Entry","servers":[{"url":"https://api.agentmail.to"},{"url":"https://x402.api.agentmail.to"},{"url":"https://mpp.api.agentmail.to"},{"url":"https://api.agentmail.eu"}],"security":[{"BearerAuth":[]},{"TokenAuth":[]}],"x-fern-sdk-group-name":["lists"],"x-fern-sdk-method-name":"delete"}},"/v0/metrics/events":{"get":{"description":"Counts of email events (sent, delivered, bounced, etc.) over time for\nthe organization. Defaults to the last 24 hours; `start` must be within\nthe last 90 days, and a future `end` is clamped to now. Omit `period`\nfor individual event counts, or set it to sum counts into buckets of\nthat many seconds.\n\n**CLI:**\n```bash\nagentmail metrics query-events\n```","operationId":"metrics_queryEvents","tags":["Metrics"],"parameters":[{"name":"event_types","in":"query","required":false,"schema":{"$ref":"#/components/schemas/MetricEventTypes","nullable":true}},{"name":"start","in":"query","required":false,"schema":{"$ref":"#/components/schemas/Start","nullable":true}},{"name":"end","in":"query","required":false,"schema":{"$ref":"#/components/schemas/End","nullable":true}},{"name":"period","in":"query","required":false,"schema":{"$ref":"#/components/schemas/Period","nullable":true}},{"name":"limit","in":"query","required":false,"schema":{"$ref":"#/components/schemas/MetricLimit","nullable":true}},{"name":"descending","in":"query","required":false,"schema":{"$ref":"#/components/schemas/Descending","nullable":true}}],"responses":{"200":{"description":"","content":{"application/json":{"schema":{"$ref":"#/components/schemas/QueryMetricsResponse"}}}},"400":{"description":"","content":{"application/json":{"schema":{"$ref":"#/components/schemas/ValidationErrorResponse"}}}}},"summary":"Query Events","servers":[{"url":"https://api.agentmail.to"},{"url":"https://x402.api.agentmail.to"},{"url":"https://mpp.api.agentmail.to"},{"url":"https://api.agentmail.eu"}],"security":[{"BearerAuth":[]},{"TokenAuth":[]}],"x-fern-sdk-group-name":["metrics"],"x-fern-sdk-method-name":"query-events"}},"/v0/metrics/usage":{"get":{"description":"Cumulative usage series for the organization. Each point is the running\ntotal of the usage type at that timestamp, not the change within the\nbucket. Defaults to the last 24 hours; `start` must be within the last\n90 days, and a future `end` is clamped to now. The range divided by\n`period` must not exceed 1000 buckets.","operationId":"metrics_queryUsage","tags":["Metrics"],"parameters":[{"name":"usage_types","in":"query","required":false,"schema":{"$ref":"#/components/schemas/UsageTypes","nullable":true}},{"name":"start","in":"query","required":false,"schema":{"$ref":"#/components/schemas/Start","nullable":true}},{"name":"end","in":"query","required":false,"schema":{"$ref":"#/components/schemas/End","nullable":true}},{"name":"period","in":"query","required":false,"schema":{"$ref":"#/components/schemas/Period","nullable":true}},{"name":"limit","in":"query","required":false,"schema":{"$ref":"#/components/schemas/MetricLimit","nullable":true}},{"name":"descending","in":"query","required":false,"schema":{"$ref":"#/components/schemas/Descending","nullable":true}}],"responses":{"200":{"description":"","content":{"application/json":{"schema":{"$ref":"#/components/schemas/QueryUsageResponse"}}}},"400":{"description":"","content":{"application/json":{"schema":{"$ref":"#/components/schemas/ValidationErrorResponse"}}}}},"summary":"Query Usage","servers":[{"url":"https://api.agentmail.to"},{"url":"https://x402.api.agentmail.to"},{"url":"https://mpp.api.agentmail.to"},{"url":"https://api.agentmail.eu"}],"security":[{"BearerAuth":[]},{"TokenAuth":[]}],"x-fern-sdk-group-name":["metrics"],"x-fern-sdk-method-name":"query-usage"}},"/v0/organizations":{"get":{"description":"Returns the organization for the authenticated API key (usage limits, counts, and billing metadata).\n\n**CLI:**\n```bash\nagentmail organizations get\n```","operationId":"organizations_get","tags":["Organizations"],"parameters":[],"responses":{"200":{"description":"","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Organization"}}}}},"summary":"Get Organization","servers":[{"url":"https://api.agentmail.to"},{"url":"https://x402.api.agentmail.to"},{"url":"https://mpp.api.agentmail.to"},{"url":"https://api.agentmail.eu"}],"security":[{"BearerAuth":[]},{"TokenAuth":[]}],"x-fern-sdk-group-name":["organizations"],"x-fern-sdk-method-name":"get"}},"/v0/pods/{pod_id}/api-keys":{"get":{"description":"**CLI:**\n```bash\nagentmail pods api-keys list --pod-id \n```","operationId":"pods_apiKeys_list","tags":["PodsApiKeys"],"parameters":[{"name":"pod_id","in":"path","required":true,"schema":{"$ref":"#/components/schemas/podsPodId"}},{"name":"limit","in":"query","required":false,"schema":{"$ref":"#/components/schemas/Limit","nullable":true}},{"name":"page_token","in":"query","required":false,"schema":{"$ref":"#/components/schemas/PageToken","nullable":true}}],"responses":{"200":{"description":"","content":{"application/json":{"schema":{"$ref":"#/components/schemas/ListApiKeysResponse"}}}},"404":{"description":"","content":{"application/json":{"schema":{"$ref":"#/components/schemas/ErrorResponse"}}}}},"summary":"List API Keys","servers":[{"url":"https://api.agentmail.to"},{"url":"https://x402.api.agentmail.to"},{"url":"https://mpp.api.agentmail.to"},{"url":"https://api.agentmail.eu"}],"security":[{"BearerAuth":[]},{"TokenAuth":[]}],"x-fern-sdk-group-name":["pods","apiKeys"],"x-fern-sdk-method-name":"list"},"post":{"description":"**CLI:**\n```bash\nagentmail pods api-keys create --pod-id --name \"My Key\"\n```","operationId":"pods_apiKeys_create","tags":["PodsApiKeys"],"parameters":[{"name":"pod_id","in":"path","required":true,"schema":{"$ref":"#/components/schemas/podsPodId"}}],"responses":{"200":{"description":"","content":{"application/json":{"schema":{"$ref":"#/components/schemas/CreateApiKeyResponse"}}}},"400":{"description":"","content":{"application/json":{"schema":{"$ref":"#/components/schemas/ValidationErrorResponse"}}}},"404":{"description":"","content":{"application/json":{"schema":{"$ref":"#/components/schemas/ErrorResponse"}}}}},"summary":"Create API Key","servers":[{"url":"https://api.agentmail.to"},{"url":"https://x402.api.agentmail.to"},{"url":"https://mpp.api.agentmail.to"},{"url":"https://api.agentmail.eu"}],"security":[{"BearerAuth":[]},{"TokenAuth":[]}],"requestBody":{"required":true,"content":{"application/json":{"schema":{"$ref":"#/components/schemas/CreateApiKeyRequest"}}}},"x-fern-sdk-group-name":["pods","apiKeys"],"x-fern-sdk-method-name":"create"}},"/v0/pods/{pod_id}/api-keys/{api_key_id}":{"delete":{"description":"**CLI:**\n```bash\nagentmail pods api-keys delete --pod-id --api-key-id \n```","operationId":"pods_apiKeys_delete","tags":["PodsApiKeys"],"parameters":[{"name":"pod_id","in":"path","required":true,"schema":{"$ref":"#/components/schemas/podsPodId"}},{"name":"api_key_id","in":"path","required":true,"schema":{"$ref":"#/components/schemas/ApiKeyId"}}],"responses":{"204":{"description":""},"404":{"description":"","content":{"application/json":{"schema":{"$ref":"#/components/schemas/ErrorResponse"}}}}},"summary":"Delete API Key","servers":[{"url":"https://api.agentmail.to"},{"url":"https://x402.api.agentmail.to"},{"url":"https://mpp.api.agentmail.to"},{"url":"https://api.agentmail.eu"}],"security":[{"BearerAuth":[]},{"TokenAuth":[]}],"x-fern-sdk-group-name":["pods","apiKeys"],"x-fern-sdk-method-name":"delete"}},"/v0/pods/{pod_id}/domains":{"get":{"description":"**CLI:**\n```bash\nagentmail pods domains list --pod-id \n```","operationId":"pods_domains_list","tags":["PodsDomains"],"parameters":[{"name":"pod_id","in":"path","required":true,"schema":{"$ref":"#/components/schemas/podsPodId"}},{"name":"limit","in":"query","required":false,"schema":{"$ref":"#/components/schemas/Limit","nullable":true}},{"name":"page_token","in":"query","required":false,"schema":{"$ref":"#/components/schemas/PageToken","nullable":true}},{"name":"ascending","in":"query","required":false,"schema":{"$ref":"#/components/schemas/Ascending","nullable":true}}],"responses":{"200":{"description":"","content":{"application/json":{"schema":{"$ref":"#/components/schemas/ListDomainsResponse"}}}},"404":{"description":"","content":{"application/json":{"schema":{"$ref":"#/components/schemas/ErrorResponse"}}}}},"summary":"List Domains","servers":[{"url":"https://api.agentmail.to"},{"url":"https://x402.api.agentmail.to"},{"url":"https://mpp.api.agentmail.to"},{"url":"https://api.agentmail.eu"}],"security":[{"BearerAuth":[]},{"TokenAuth":[]}],"x-fern-sdk-group-name":["pods","domains"],"x-fern-sdk-method-name":"list"},"post":{"description":"**CLI:**\n```bash\nagentmail pods domains create --pod-id --domain example.com\n```","operationId":"pods_domains_create","tags":["PodsDomains"],"parameters":[{"name":"pod_id","in":"path","required":true,"schema":{"$ref":"#/components/schemas/podsPodId"}}],"responses":{"200":{"description":"","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Domain"}}}},"400":{"description":"","content":{"application/json":{"schema":{"$ref":"#/components/schemas/ValidationErrorResponse"}}}}},"summary":"Create Domain","servers":[{"url":"https://api.agentmail.to"},{"url":"https://x402.api.agentmail.to"},{"url":"https://mpp.api.agentmail.to"},{"url":"https://api.agentmail.eu"}],"security":[{"BearerAuth":[]},{"TokenAuth":[]}],"requestBody":{"required":true,"content":{"application/json":{"schema":{"$ref":"#/components/schemas/CreateDomainRequest"}}}},"x-fern-sdk-group-name":["pods","domains"],"x-fern-sdk-method-name":"create"}},"/v0/pods/{pod_id}/domains/{domain_id}":{"get":{"description":"**CLI:**\n```bash\nagentmail pods domains get --pod-id --domain-id \n```","operationId":"pods_domains_get","tags":["PodsDomains"],"parameters":[{"name":"pod_id","in":"path","required":true,"schema":{"$ref":"#/components/schemas/podsPodId"}},{"name":"domain_id","in":"path","required":true,"schema":{"$ref":"#/components/schemas/DomainId"}}],"responses":{"200":{"description":"","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Domain"}}}},"404":{"description":"","content":{"application/json":{"schema":{"$ref":"#/components/schemas/ErrorResponse"}}}}},"summary":"Get Domain","servers":[{"url":"https://api.agentmail.to"},{"url":"https://x402.api.agentmail.to"},{"url":"https://mpp.api.agentmail.to"},{"url":"https://api.agentmail.eu"}],"security":[{"BearerAuth":[]},{"TokenAuth":[]}],"x-fern-sdk-group-name":["pods","domains"],"x-fern-sdk-method-name":"get"},"patch":{"description":"**CLI:**\n```bash\nagentmail pods domains update --pod-id --domain-id \n```","operationId":"pods_domains_update","tags":["PodsDomains"],"parameters":[{"name":"pod_id","in":"path","required":true,"schema":{"$ref":"#/components/schemas/podsPodId"}},{"name":"domain_id","in":"path","required":true,"schema":{"$ref":"#/components/schemas/DomainId"}}],"responses":{"200":{"description":"","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Domain"}}}},"404":{"description":"","content":{"application/json":{"schema":{"$ref":"#/components/schemas/ErrorResponse"}}}}},"summary":"Update Domain","servers":[{"url":"https://api.agentmail.to"},{"url":"https://x402.api.agentmail.to"},{"url":"https://mpp.api.agentmail.to"},{"url":"https://api.agentmail.eu"}],"security":[{"BearerAuth":[]},{"TokenAuth":[]}],"requestBody":{"required":true,"content":{"application/json":{"schema":{"$ref":"#/components/schemas/UpdateDomainRequest"}}}},"x-fern-sdk-group-name":["pods","domains"],"x-fern-sdk-method-name":"update"},"delete":{"description":"**CLI:**\n```bash\nagentmail pods domains delete --pod-id --domain-id \n```","operationId":"pods_domains_delete","tags":["PodsDomains"],"parameters":[{"name":"pod_id","in":"path","required":true,"schema":{"$ref":"#/components/schemas/podsPodId"}},{"name":"domain_id","in":"path","required":true,"schema":{"$ref":"#/components/schemas/DomainId"}}],"responses":{"204":{"description":""},"404":{"description":"","content":{"application/json":{"schema":{"$ref":"#/components/schemas/ErrorResponse"}}}}},"summary":"Delete Domain","servers":[{"url":"https://api.agentmail.to"},{"url":"https://x402.api.agentmail.to"},{"url":"https://mpp.api.agentmail.to"},{"url":"https://api.agentmail.eu"}],"security":[{"BearerAuth":[]},{"TokenAuth":[]}],"x-fern-sdk-group-name":["pods","domains"],"x-fern-sdk-method-name":"delete"}},"/v0/pods/{pod_id}/domains/{domain_id}/zone-file":{"get":{"description":"**CLI:**\n```bash\nagentmail pods domains get-zone-file --pod-id --domain-id \n```","operationId":"pods_domains_getZoneFile","tags":["PodsDomains"],"parameters":[{"name":"pod_id","in":"path","required":true,"schema":{"$ref":"#/components/schemas/podsPodId"}},{"name":"domain_id","in":"path","required":true,"schema":{"$ref":"#/components/schemas/DomainId"}}],"responses":{"204":{"description":""},"404":{"description":"","content":{"application/json":{"schema":{"$ref":"#/components/schemas/ErrorResponse"}}}}},"summary":"Get Zone File","servers":[{"url":"https://api.agentmail.to"},{"url":"https://x402.api.agentmail.to"},{"url":"https://mpp.api.agentmail.to"},{"url":"https://api.agentmail.eu"}],"security":[{"BearerAuth":[]},{"TokenAuth":[]}],"x-fern-sdk-group-name":["pods","domains"],"x-fern-sdk-method-name":"get-zone-file"}},"/v0/pods/{pod_id}/domains/{domain_id}/verify":{"post":{"description":"**CLI:**\n```bash\nagentmail pods domains verify --pod-id --domain-id \n```","operationId":"pods_domains_verify","tags":["PodsDomains"],"parameters":[{"name":"pod_id","in":"path","required":true,"schema":{"$ref":"#/components/schemas/podsPodId"}},{"name":"domain_id","in":"path","required":true,"schema":{"$ref":"#/components/schemas/DomainId"}}],"responses":{"204":{"description":""},"404":{"description":"","content":{"application/json":{"schema":{"$ref":"#/components/schemas/ErrorResponse"}}}}},"summary":"Verify Domain","servers":[{"url":"https://api.agentmail.to"},{"url":"https://x402.api.agentmail.to"},{"url":"https://mpp.api.agentmail.to"},{"url":"https://api.agentmail.eu"}],"security":[{"BearerAuth":[]},{"TokenAuth":[]}],"x-fern-sdk-group-name":["pods","domains"],"x-fern-sdk-method-name":"verify"}},"/v0/pods/{pod_id}/drafts":{"get":{"description":"**CLI:**\n```bash\nagentmail pods drafts list --pod-id \n```","operationId":"pods_drafts_list","tags":["PodsDrafts"],"parameters":[{"name":"pod_id","in":"path","required":true,"schema":{"$ref":"#/components/schemas/podsPodId"}},{"name":"limit","in":"query","required":false,"schema":{"$ref":"#/components/schemas/Limit","nullable":true}},{"name":"page_token","in":"query","required":false,"schema":{"$ref":"#/components/schemas/PageToken","nullable":true}},{"name":"labels","in":"query","required":false,"schema":{"$ref":"#/components/schemas/Labels","nullable":true}},{"name":"before","in":"query","required":false,"schema":{"$ref":"#/components/schemas/Before","nullable":true}},{"name":"after","in":"query","required":false,"schema":{"$ref":"#/components/schemas/After","nullable":true}},{"name":"ascending","in":"query","required":false,"schema":{"$ref":"#/components/schemas/Ascending","nullable":true}}],"responses":{"200":{"description":"","content":{"application/json":{"schema":{"$ref":"#/components/schemas/ListDraftsResponse"}}}},"404":{"description":"","content":{"application/json":{"schema":{"$ref":"#/components/schemas/ErrorResponse"}}}}},"summary":"List Drafts","servers":[{"url":"https://api.agentmail.to"},{"url":"https://x402.api.agentmail.to"},{"url":"https://mpp.api.agentmail.to"},{"url":"https://api.agentmail.eu"}],"security":[{"BearerAuth":[]},{"TokenAuth":[]}],"x-fern-sdk-group-name":["pods","drafts"],"x-fern-sdk-method-name":"list"}},"/v0/pods/{pod_id}/drafts/{draft_id}":{"get":{"description":"**CLI:**\n```bash\nagentmail pods drafts get --pod-id --draft-id \n```","operationId":"pods_drafts_get","tags":["PodsDrafts"],"parameters":[{"name":"pod_id","in":"path","required":true,"schema":{"$ref":"#/components/schemas/podsPodId"}},{"name":"draft_id","in":"path","required":true,"schema":{"$ref":"#/components/schemas/DraftId"}}],"responses":{"200":{"description":"","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Draft"}}}},"404":{"description":"","content":{"application/json":{"schema":{"$ref":"#/components/schemas/ErrorResponse"}}}}},"summary":"Get Draft","servers":[{"url":"https://api.agentmail.to"},{"url":"https://x402.api.agentmail.to"},{"url":"https://mpp.api.agentmail.to"},{"url":"https://api.agentmail.eu"}],"security":[{"BearerAuth":[]},{"TokenAuth":[]}],"x-fern-sdk-group-name":["pods","drafts"],"x-fern-sdk-method-name":"get"}},"/v0/pods/{pod_id}/drafts/{draft_id}/attachments/{attachment_id}":{"get":{"description":"**CLI:**\n```bash\nagentmail pods drafts get-attachment --pod-id --draft-id --attachment-id \n```","operationId":"pods_drafts_getAttachment","tags":["PodsDrafts"],"parameters":[{"name":"pod_id","in":"path","required":true,"schema":{"$ref":"#/components/schemas/podsPodId"}},{"name":"draft_id","in":"path","required":true,"schema":{"$ref":"#/components/schemas/DraftId"}},{"name":"attachment_id","in":"path","required":true,"schema":{"$ref":"#/components/schemas/AttachmentId"}}],"responses":{"200":{"description":"","content":{"application/json":{"schema":{"$ref":"#/components/schemas/AttachmentResponse"}}}},"404":{"description":"","content":{"application/json":{"schema":{"$ref":"#/components/schemas/ErrorResponse"}}}}},"summary":"Get Attachment","servers":[{"url":"https://api.agentmail.to"},{"url":"https://x402.api.agentmail.to"},{"url":"https://mpp.api.agentmail.to"},{"url":"https://api.agentmail.eu"}],"security":[{"BearerAuth":[]},{"TokenAuth":[]}],"x-fern-sdk-group-name":["pods","drafts"],"x-fern-sdk-method-name":"get-attachment"}},"/v0/pods/{pod_id}/inboxes":{"get":{"description":"**CLI:**\n```bash\nagentmail pods inboxes list --pod-id \n```","operationId":"pods_inboxes_list","tags":["PodsInboxes"],"parameters":[{"name":"pod_id","in":"path","required":true,"schema":{"$ref":"#/components/schemas/podsPodId"}},{"name":"limit","in":"query","required":false,"schema":{"$ref":"#/components/schemas/Limit","nullable":true}},{"name":"page_token","in":"query","required":false,"schema":{"$ref":"#/components/schemas/PageToken","nullable":true}},{"name":"ascending","in":"query","required":false,"schema":{"$ref":"#/components/schemas/Ascending","nullable":true}}],"responses":{"200":{"description":"","content":{"application/json":{"schema":{"$ref":"#/components/schemas/inboxesListInboxesResponse"}}}},"404":{"description":"","content":{"application/json":{"schema":{"$ref":"#/components/schemas/ErrorResponse"}}}}},"summary":"List Inboxes","servers":[{"url":"https://api.agentmail.to"},{"url":"https://x402.api.agentmail.to"},{"url":"https://mpp.api.agentmail.to"},{"url":"https://api.agentmail.eu"}],"security":[{"BearerAuth":[]},{"TokenAuth":[]}],"x-fern-sdk-group-name":["pods","inboxes"],"x-fern-sdk-method-name":"list"},"post":{"description":"**CLI:**\n```bash\nagentmail pods inboxes create --pod-id --username myagent --domain example.com\n```","operationId":"pods_inboxes_create","tags":["PodsInboxes"],"parameters":[{"name":"pod_id","in":"path","required":true,"schema":{"$ref":"#/components/schemas/podsPodId"}}],"responses":{"200":{"description":"","content":{"application/json":{"schema":{"$ref":"#/components/schemas/inboxesInbox"}}}},"400":{"description":"","content":{"application/json":{"schema":{"$ref":"#/components/schemas/ValidationErrorResponse"}}}},"422":{"description":"","content":{"application/json":{"schema":{"$ref":"#/components/schemas/ErrorResponse"}}}}},"summary":"Create Inbox","servers":[{"url":"https://api.agentmail.to"},{"url":"https://x402.api.agentmail.to"},{"url":"https://mpp.api.agentmail.to"},{"url":"https://api.agentmail.eu"}],"security":[{"BearerAuth":[]},{"TokenAuth":[]}],"requestBody":{"required":true,"content":{"application/json":{"schema":{"$ref":"#/components/schemas/inboxesCreateInboxRequest"}}}},"x-fern-sdk-group-name":["pods","inboxes"],"x-fern-sdk-method-name":"create"}},"/v0/pods/{pod_id}/inboxes/{inbox_id}":{"get":{"description":"**CLI:**\n```bash\nagentmail pods inboxes get --pod-id --inbox-id \n```","operationId":"pods_inboxes_get","tags":["PodsInboxes"],"parameters":[{"name":"pod_id","in":"path","required":true,"schema":{"$ref":"#/components/schemas/podsPodId"}},{"name":"inbox_id","in":"path","required":true,"schema":{"$ref":"#/components/schemas/inboxesInboxId"}}],"responses":{"200":{"description":"","content":{"application/json":{"schema":{"$ref":"#/components/schemas/inboxesInbox"}}}},"404":{"description":"","content":{"application/json":{"schema":{"$ref":"#/components/schemas/ErrorResponse"}}}}},"summary":"Get Inbox","servers":[{"url":"https://api.agentmail.to"},{"url":"https://x402.api.agentmail.to"},{"url":"https://mpp.api.agentmail.to"},{"url":"https://api.agentmail.eu"}],"security":[{"BearerAuth":[]},{"TokenAuth":[]}],"x-fern-sdk-group-name":["pods","inboxes"],"x-fern-sdk-method-name":"get"},"patch":{"description":"**CLI:**\n```bash\nagentmail pods inboxes update --pod-id --inbox-id \n```","operationId":"pods_inboxes_update","tags":["PodsInboxes"],"parameters":[{"name":"pod_id","in":"path","required":true,"schema":{"$ref":"#/components/schemas/podsPodId"}},{"name":"inbox_id","in":"path","required":true,"schema":{"$ref":"#/components/schemas/inboxesInboxId"}}],"responses":{"200":{"description":"","content":{"application/json":{"schema":{"$ref":"#/components/schemas/inboxesInbox"}}}},"404":{"description":"","content":{"application/json":{"schema":{"$ref":"#/components/schemas/ErrorResponse"}}}}},"summary":"Update Inbox","servers":[{"url":"https://api.agentmail.to"},{"url":"https://x402.api.agentmail.to"},{"url":"https://mpp.api.agentmail.to"},{"url":"https://api.agentmail.eu"}],"security":[{"BearerAuth":[]},{"TokenAuth":[]}],"requestBody":{"required":true,"content":{"application/json":{"schema":{"$ref":"#/components/schemas/inboxesUpdateInboxRequest"}}}},"x-fern-sdk-group-name":["pods","inboxes"],"x-fern-sdk-method-name":"update"},"delete":{"description":"**CLI:**\n```bash\nagentmail pods inboxes delete --pod-id --inbox-id \n```","operationId":"pods_inboxes_delete","tags":["PodsInboxes"],"parameters":[{"name":"pod_id","in":"path","required":true,"schema":{"$ref":"#/components/schemas/podsPodId"}},{"name":"inbox_id","in":"path","required":true,"schema":{"$ref":"#/components/schemas/inboxesInboxId"}}],"responses":{"204":{"description":""},"404":{"description":"","content":{"application/json":{"schema":{"$ref":"#/components/schemas/ErrorResponse"}}}}},"summary":"Delete Inbox","servers":[{"url":"https://api.agentmail.to"},{"url":"https://x402.api.agentmail.to"},{"url":"https://mpp.api.agentmail.to"},{"url":"https://api.agentmail.eu"}],"security":[{"BearerAuth":[]},{"TokenAuth":[]}],"x-fern-sdk-group-name":["pods","inboxes"],"x-fern-sdk-method-name":"delete"}},"/v0/pods/{pod_id}/lists/{direction}/{type}":{"get":{"description":"**CLI:**\n```bash\nagentmail pods lists list --pod-id --direction --type \n```","operationId":"pods_lists_list","tags":["PodsLists"],"parameters":[{"name":"pod_id","in":"path","required":true,"schema":{"$ref":"#/components/schemas/podsPodId"}},{"name":"direction","in":"path","required":true,"schema":{"$ref":"#/components/schemas/Direction"}},{"name":"type","in":"path","required":true,"schema":{"$ref":"#/components/schemas/ListType"}},{"name":"limit","in":"query","required":false,"schema":{"$ref":"#/components/schemas/Limit","nullable":true}},{"name":"page_token","in":"query","required":false,"schema":{"$ref":"#/components/schemas/PageToken","nullable":true}}],"responses":{"200":{"description":"","content":{"application/json":{"schema":{"$ref":"#/components/schemas/PodListListEntriesResponse"}}}}},"summary":"List Entries","servers":[{"url":"https://api.agentmail.to"},{"url":"https://x402.api.agentmail.to"},{"url":"https://mpp.api.agentmail.to"},{"url":"https://api.agentmail.eu"}],"security":[{"BearerAuth":[]},{"TokenAuth":[]}],"x-fern-sdk-group-name":["pods","lists"],"x-fern-sdk-method-name":"list"},"post":{"description":"**CLI:**\n```bash\nagentmail pods lists create --pod-id --direction --type --entry user@example.com\n```","operationId":"pods_lists_create","tags":["PodsLists"],"parameters":[{"name":"pod_id","in":"path","required":true,"schema":{"$ref":"#/components/schemas/podsPodId"}},{"name":"direction","in":"path","required":true,"schema":{"$ref":"#/components/schemas/Direction"}},{"name":"type","in":"path","required":true,"schema":{"$ref":"#/components/schemas/ListType"}}],"responses":{"200":{"description":"","content":{"application/json":{"schema":{"$ref":"#/components/schemas/PodListEntry"}}}},"400":{"description":"","content":{"application/json":{"schema":{"$ref":"#/components/schemas/ValidationErrorResponse"}}}}},"summary":"Create List Entry","servers":[{"url":"https://api.agentmail.to"},{"url":"https://x402.api.agentmail.to"},{"url":"https://mpp.api.agentmail.to"},{"url":"https://api.agentmail.eu"}],"security":[{"BearerAuth":[]},{"TokenAuth":[]}],"requestBody":{"required":true,"content":{"application/json":{"schema":{"$ref":"#/components/schemas/CreateListEntryRequest"}}}},"x-fern-sdk-group-name":["pods","lists"],"x-fern-sdk-method-name":"create"}},"/v0/pods/{pod_id}/lists/{direction}/{type}/{entry}":{"get":{"description":"**CLI:**\n```bash\nagentmail pods lists get --pod-id --direction --type --entry \n```","operationId":"pods_lists_get","tags":["PodsLists"],"parameters":[{"name":"pod_id","in":"path","required":true,"schema":{"$ref":"#/components/schemas/podsPodId"}},{"name":"direction","in":"path","required":true,"schema":{"$ref":"#/components/schemas/Direction"}},{"name":"type","in":"path","required":true,"schema":{"$ref":"#/components/schemas/ListType"}},{"name":"entry","in":"path","description":"Email address or domain.","required":true,"schema":{"type":"string"}}],"responses":{"200":{"description":"","content":{"application/json":{"schema":{"$ref":"#/components/schemas/PodListEntry"}}}},"404":{"description":"","content":{"application/json":{"schema":{"$ref":"#/components/schemas/ErrorResponse"}}}}},"summary":"Get List Entry","servers":[{"url":"https://api.agentmail.to"},{"url":"https://x402.api.agentmail.to"},{"url":"https://mpp.api.agentmail.to"},{"url":"https://api.agentmail.eu"}],"security":[{"BearerAuth":[]},{"TokenAuth":[]}],"x-fern-sdk-group-name":["pods","lists"],"x-fern-sdk-method-name":"get"},"delete":{"description":"**CLI:**\n```bash\nagentmail pods lists delete --pod-id --direction --type --entry \n```","operationId":"pods_lists_delete","tags":["PodsLists"],"parameters":[{"name":"pod_id","in":"path","required":true,"schema":{"$ref":"#/components/schemas/podsPodId"}},{"name":"direction","in":"path","required":true,"schema":{"$ref":"#/components/schemas/Direction"}},{"name":"type","in":"path","required":true,"schema":{"$ref":"#/components/schemas/ListType"}},{"name":"entry","in":"path","description":"Email address or domain.","required":true,"schema":{"type":"string"}}],"responses":{"204":{"description":""},"404":{"description":"","content":{"application/json":{"schema":{"$ref":"#/components/schemas/ErrorResponse"}}}}},"summary":"Delete List Entry","servers":[{"url":"https://api.agentmail.to"},{"url":"https://x402.api.agentmail.to"},{"url":"https://mpp.api.agentmail.to"},{"url":"https://api.agentmail.eu"}],"security":[{"BearerAuth":[]},{"TokenAuth":[]}],"x-fern-sdk-group-name":["pods","lists"],"x-fern-sdk-method-name":"delete"}},"/v0/pods/{pod_id}/metrics/events":{"get":{"description":"Counts of email events (sent, delivered, bounced, etc.) over time for\nthe pod. Defaults to the last 24 hours; `start` must be within the last\n90 days, and a future `end` is clamped to now. Omit `period` for\nindividual event counts, or set it to sum counts into buckets of that\nmany seconds.\n\n**CLI:**\n```bash\nagentmail pods metrics query-events --pod-id \n```","operationId":"pods_metrics_queryEvents","tags":["PodsMetrics"],"parameters":[{"name":"pod_id","in":"path","required":true,"schema":{"$ref":"#/components/schemas/podsPodId"}},{"name":"event_types","in":"query","required":false,"schema":{"$ref":"#/components/schemas/MetricEventTypes","nullable":true}},{"name":"start","in":"query","required":false,"schema":{"$ref":"#/components/schemas/Start","nullable":true}},{"name":"end","in":"query","required":false,"schema":{"$ref":"#/components/schemas/End","nullable":true}},{"name":"period","in":"query","required":false,"schema":{"$ref":"#/components/schemas/Period","nullable":true}},{"name":"limit","in":"query","required":false,"schema":{"$ref":"#/components/schemas/MetricLimit","nullable":true}},{"name":"descending","in":"query","required":false,"schema":{"$ref":"#/components/schemas/Descending","nullable":true}}],"responses":{"200":{"description":"","content":{"application/json":{"schema":{"$ref":"#/components/schemas/QueryMetricsResponse"}}}},"400":{"description":"","content":{"application/json":{"schema":{"$ref":"#/components/schemas/ValidationErrorResponse"}}}}},"summary":"Query Events","servers":[{"url":"https://api.agentmail.to"},{"url":"https://x402.api.agentmail.to"},{"url":"https://mpp.api.agentmail.to"},{"url":"https://api.agentmail.eu"}],"security":[{"BearerAuth":[]},{"TokenAuth":[]}],"x-fern-sdk-group-name":["pods","metrics"],"x-fern-sdk-method-name":"query-events"}},"/v0/pods/{pod_id}/metrics/usage":{"get":{"description":"Cumulative usage series for the pod. Each point is the running total of\nthe usage type at that timestamp, not the change within the bucket.\nPod-scoped queries carry every usage type except `pod_count`; requested\ntypes that don't apply to the scope are ignored. Defaults to the last\n24 hours; `start` must be within the last 90 days, and a future `end`\nis clamped to now. The range divided by `period` must not exceed 1000\nbuckets.","operationId":"pods_metrics_queryUsage","tags":["PodsMetrics"],"parameters":[{"name":"pod_id","in":"path","required":true,"schema":{"$ref":"#/components/schemas/podsPodId"}},{"name":"usage_types","in":"query","required":false,"schema":{"$ref":"#/components/schemas/UsageTypes","nullable":true}},{"name":"start","in":"query","required":false,"schema":{"$ref":"#/components/schemas/Start","nullable":true}},{"name":"end","in":"query","required":false,"schema":{"$ref":"#/components/schemas/End","nullable":true}},{"name":"period","in":"query","required":false,"schema":{"$ref":"#/components/schemas/Period","nullable":true}},{"name":"limit","in":"query","required":false,"schema":{"$ref":"#/components/schemas/MetricLimit","nullable":true}},{"name":"descending","in":"query","required":false,"schema":{"$ref":"#/components/schemas/Descending","nullable":true}}],"responses":{"200":{"description":"","content":{"application/json":{"schema":{"$ref":"#/components/schemas/QueryUsageResponse"}}}},"400":{"description":"","content":{"application/json":{"schema":{"$ref":"#/components/schemas/ValidationErrorResponse"}}}}},"summary":"Query Usage","servers":[{"url":"https://api.agentmail.to"},{"url":"https://x402.api.agentmail.to"},{"url":"https://mpp.api.agentmail.to"},{"url":"https://api.agentmail.eu"}],"security":[{"BearerAuth":[]},{"TokenAuth":[]}],"x-fern-sdk-group-name":["pods","metrics"],"x-fern-sdk-method-name":"query-usage"}},"/v0/pods/{pod_id}/threads":{"get":{"description":"Lists threads in the pod, most recent first. Pass `senders`,\n`recipients`, or `subject` to filter by substring. Filtered requests are\nserved by search, which caps `limit` at 100. For relevance-ranked\nfull-text search, use `Search Threads`.\n\n**CLI:**\n```bash\nagentmail pods threads list --pod-id \n```","operationId":"pods_threads_list","tags":["PodsThreads"],"parameters":[{"name":"pod_id","in":"path","required":true,"schema":{"$ref":"#/components/schemas/podsPodId"}},{"name":"limit","in":"query","required":false,"schema":{"$ref":"#/components/schemas/Limit","nullable":true}},{"name":"page_token","in":"query","required":false,"schema":{"$ref":"#/components/schemas/PageToken","nullable":true}},{"name":"labels","in":"query","required":false,"schema":{"$ref":"#/components/schemas/Labels","nullable":true}},{"name":"before","in":"query","required":false,"schema":{"$ref":"#/components/schemas/Before","nullable":true}},{"name":"after","in":"query","required":false,"schema":{"$ref":"#/components/schemas/After","nullable":true}},{"name":"ascending","in":"query","required":false,"schema":{"$ref":"#/components/schemas/Ascending","nullable":true}},{"name":"include_spam","in":"query","required":false,"schema":{"$ref":"#/components/schemas/IncludeSpam","nullable":true}},{"name":"include_blocked","in":"query","required":false,"schema":{"$ref":"#/components/schemas/IncludeBlocked","nullable":true}},{"name":"include_unauthenticated","in":"query","required":false,"schema":{"$ref":"#/components/schemas/IncludeUnauthenticated","nullable":true}},{"name":"include_trash","in":"query","required":false,"schema":{"$ref":"#/components/schemas/IncludeTrash","nullable":true}},{"name":"senders","in":"query","description":"Filter to threads whose senders contain this value (substring match). Repeatable; all values must match.","required":false,"schema":{"type":"array","items":{"type":"string"},"nullable":true}},{"name":"recipients","in":"query","description":"Filter to threads whose recipients contain this value (substring match). Repeatable; all values must match.","required":false,"schema":{"type":"array","items":{"type":"string"},"nullable":true}},{"name":"subject","in":"query","description":"Filter to threads whose subject contains this value (substring match). Repeatable; all values must match.","required":false,"schema":{"type":"array","items":{"type":"string"},"nullable":true}}],"responses":{"200":{"description":"","content":{"application/json":{"schema":{"$ref":"#/components/schemas/ListThreadsResponse"}}}},"404":{"description":"","content":{"application/json":{"schema":{"$ref":"#/components/schemas/ErrorResponse"}}}}},"summary":"List Threads","servers":[{"url":"https://api.agentmail.to"},{"url":"https://x402.api.agentmail.to"},{"url":"https://mpp.api.agentmail.to"},{"url":"https://api.agentmail.eu"}],"security":[{"BearerAuth":[]},{"TokenAuth":[]}],"x-fern-sdk-group-name":["pods","threads"],"x-fern-sdk-method-name":"list"}},"/v0/pods/{pod_id}/threads/search":{"get":{"description":"Full-text search across threads in the pod, ranked by relevance. The\nquery is matched against senders, recipients, and subject (substring)\nand the message body (tokenized full text). Spam, trash, blocked, and\nunauthenticated threads are always excluded. `limit` cannot exceed 100.","operationId":"pods_threads_search","tags":["PodsThreads"],"parameters":[{"name":"pod_id","in":"path","required":true,"schema":{"$ref":"#/components/schemas/podsPodId"}},{"name":"q","in":"query","required":true,"schema":{"$ref":"#/components/schemas/Query"}},{"name":"limit","in":"query","required":false,"schema":{"$ref":"#/components/schemas/Limit","nullable":true}},{"name":"page_token","in":"query","required":false,"schema":{"$ref":"#/components/schemas/PageToken","nullable":true}},{"name":"before","in":"query","required":false,"schema":{"$ref":"#/components/schemas/Before","nullable":true}},{"name":"after","in":"query","required":false,"schema":{"$ref":"#/components/schemas/After","nullable":true}}],"responses":{"200":{"description":"","content":{"application/json":{"schema":{"$ref":"#/components/schemas/SearchThreadsResponse"}}}},"400":{"description":"","content":{"application/json":{"schema":{"$ref":"#/components/schemas/ValidationErrorResponse"}}}},"404":{"description":"","content":{"application/json":{"schema":{"$ref":"#/components/schemas/ErrorResponse"}}}}},"summary":"Search Threads","servers":[{"url":"https://api.agentmail.to"},{"url":"https://x402.api.agentmail.to"},{"url":"https://mpp.api.agentmail.to"},{"url":"https://api.agentmail.eu"}],"security":[{"BearerAuth":[]},{"TokenAuth":[]}],"x-fern-sdk-group-name":["pods","threads"],"x-fern-sdk-method-name":"search"}},"/v0/pods/{pod_id}/threads/{thread_id}":{"get":{"description":"**CLI:**\n```bash\nagentmail pods threads get --pod-id --thread-id \n```","operationId":"pods_threads_get","tags":["PodsThreads"],"parameters":[{"name":"pod_id","in":"path","required":true,"schema":{"$ref":"#/components/schemas/podsPodId"}},{"name":"thread_id","in":"path","required":true,"schema":{"$ref":"#/components/schemas/ThreadId"}}],"responses":{"200":{"description":"","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Thread"}}}},"404":{"description":"","content":{"application/json":{"schema":{"$ref":"#/components/schemas/ErrorResponse"}}}}},"summary":"Get Thread","servers":[{"url":"https://api.agentmail.to"},{"url":"https://x402.api.agentmail.to"},{"url":"https://mpp.api.agentmail.to"},{"url":"https://api.agentmail.eu"}],"security":[{"BearerAuth":[]},{"TokenAuth":[]}],"x-fern-sdk-group-name":["pods","threads"],"x-fern-sdk-method-name":"get"},"patch":{"description":"Updates thread labels. Cannot add or remove system labels (sent, received, bounced, etc.). Rejects requests with a `422` for threads with 100 or more messages.","operationId":"pods_threads_update","tags":["PodsThreads"],"parameters":[{"name":"pod_id","in":"path","required":true,"schema":{"$ref":"#/components/schemas/podsPodId"}},{"name":"thread_id","in":"path","required":true,"schema":{"$ref":"#/components/schemas/ThreadId"}}],"responses":{"200":{"description":"","content":{"application/json":{"schema":{"$ref":"#/components/schemas/UpdateThreadResponse"}}}},"400":{"description":"","content":{"application/json":{"schema":{"$ref":"#/components/schemas/ValidationErrorResponse"}}}},"404":{"description":"","content":{"application/json":{"schema":{"$ref":"#/components/schemas/ErrorResponse"}}}},"422":{"description":"","content":{"application/json":{"schema":{"$ref":"#/components/schemas/ErrorResponse"}}}}},"summary":"Update Thread","servers":[{"url":"https://api.agentmail.to"},{"url":"https://x402.api.agentmail.to"},{"url":"https://mpp.api.agentmail.to"},{"url":"https://api.agentmail.eu"}],"security":[{"BearerAuth":[]},{"TokenAuth":[]}],"requestBody":{"required":true,"content":{"application/json":{"schema":{"$ref":"#/components/schemas/UpdateThreadRequest"}}}},"x-fern-sdk-group-name":["pods","threads"],"x-fern-sdk-method-name":"update"},"delete":{"description":"Permanently deletes a thread and all of its messages.\n\n**CLI:**\n```bash\nagentmail pods threads delete --pod-id --thread-id \n```","operationId":"pods_threads_delete","tags":["PodsThreads"],"parameters":[{"name":"pod_id","in":"path","required":true,"schema":{"$ref":"#/components/schemas/podsPodId"}},{"name":"thread_id","in":"path","required":true,"schema":{"$ref":"#/components/schemas/ThreadId"}}],"responses":{"204":{"description":""},"404":{"description":"","content":{"application/json":{"schema":{"$ref":"#/components/schemas/ErrorResponse"}}}}},"summary":"Delete Thread","servers":[{"url":"https://api.agentmail.to"},{"url":"https://x402.api.agentmail.to"},{"url":"https://mpp.api.agentmail.to"},{"url":"https://api.agentmail.eu"}],"security":[{"BearerAuth":[]},{"TokenAuth":[]}],"x-fern-sdk-group-name":["pods","threads"],"x-fern-sdk-method-name":"delete"}},"/v0/pods/{pod_id}/threads/{thread_id}/attachments/{attachment_id}":{"get":{"description":"**CLI:**\n```bash\nagentmail pods threads get-attachment --pod-id --thread-id --attachment-id \n```","operationId":"pods_threads_getAttachment","tags":["PodsThreads"],"parameters":[{"name":"pod_id","in":"path","required":true,"schema":{"$ref":"#/components/schemas/podsPodId"}},{"name":"thread_id","in":"path","required":true,"schema":{"$ref":"#/components/schemas/ThreadId"}},{"name":"attachment_id","in":"path","required":true,"schema":{"$ref":"#/components/schemas/AttachmentId"}}],"responses":{"200":{"description":"","content":{"application/json":{"schema":{"$ref":"#/components/schemas/AttachmentResponse"}}}},"404":{"description":"","content":{"application/json":{"schema":{"$ref":"#/components/schemas/ErrorResponse"}}}}},"summary":"Get Attachment","servers":[{"url":"https://api.agentmail.to"},{"url":"https://x402.api.agentmail.to"},{"url":"https://mpp.api.agentmail.to"},{"url":"https://api.agentmail.eu"}],"security":[{"BearerAuth":[]},{"TokenAuth":[]}],"x-fern-sdk-group-name":["pods","threads"],"x-fern-sdk-method-name":"get-attachment"}},"/v0/pods/{pod_id}/webhooks":{"get":{"description":"**CLI:**\n```bash\nagentmail pods webhooks list --pod-id \n```","operationId":"pods_webhooks_list","tags":["PodsWebhooks"],"parameters":[{"name":"pod_id","in":"path","required":true,"schema":{"$ref":"#/components/schemas/podsPodId"}},{"name":"limit","in":"query","required":false,"schema":{"$ref":"#/components/schemas/Limit","nullable":true}},{"name":"page_token","in":"query","required":false,"schema":{"$ref":"#/components/schemas/PageToken","nullable":true}},{"name":"ascending","in":"query","required":false,"schema":{"$ref":"#/components/schemas/Ascending","nullable":true}}],"responses":{"200":{"description":"","content":{"application/json":{"schema":{"$ref":"#/components/schemas/webhooksListWebhooksResponse"}}}}},"summary":"List Webhooks","servers":[{"url":"https://api.agentmail.to"},{"url":"https://x402.api.agentmail.to"},{"url":"https://mpp.api.agentmail.to"},{"url":"https://api.agentmail.eu"}],"security":[{"BearerAuth":[]},{"TokenAuth":[]}],"x-fern-sdk-group-name":["pods","webhooks"],"x-fern-sdk-method-name":"list"},"post":{"description":"Create a webhook scoped to this pod.\n\n**CLI:**\n```bash\nagentmail pods webhooks create --pod-id --url https://example.com/webhook --event-types message.received\n```","operationId":"pods_webhooks_create","tags":["PodsWebhooks"],"parameters":[{"name":"pod_id","in":"path","required":true,"schema":{"$ref":"#/components/schemas/podsPodId"}}],"responses":{"200":{"description":"","content":{"application/json":{"schema":{"$ref":"#/components/schemas/webhooksWebhook"}}}},"400":{"description":"","content":{"application/json":{"schema":{"$ref":"#/components/schemas/ValidationErrorResponse"}}}}},"summary":"Create Webhook","servers":[{"url":"https://api.agentmail.to"},{"url":"https://x402.api.agentmail.to"},{"url":"https://mpp.api.agentmail.to"},{"url":"https://api.agentmail.eu"}],"security":[{"BearerAuth":[]},{"TokenAuth":[]}],"requestBody":{"required":true,"content":{"application/json":{"schema":{"$ref":"#/components/schemas/webhooksCreatePodWebhookRequest"}}}},"x-fern-sdk-group-name":["pods","webhooks"],"x-fern-sdk-method-name":"create"}},"/v0/pods/{pod_id}/webhooks/{webhook_id}":{"get":{"description":"**CLI:**\n```bash\nagentmail pods webhooks get --pod-id --webhook-id \n```","operationId":"pods_webhooks_get","tags":["PodsWebhooks"],"parameters":[{"name":"pod_id","in":"path","required":true,"schema":{"$ref":"#/components/schemas/podsPodId"}},{"name":"webhook_id","in":"path","required":true,"schema":{"$ref":"#/components/schemas/webhooksWebhookId"}}],"responses":{"200":{"description":"","content":{"application/json":{"schema":{"$ref":"#/components/schemas/webhooksWebhook"}}}},"404":{"description":"","content":{"application/json":{"schema":{"$ref":"#/components/schemas/ErrorResponse"}}}}},"summary":"Get Webhook","servers":[{"url":"https://api.agentmail.to"},{"url":"https://x402.api.agentmail.to"},{"url":"https://mpp.api.agentmail.to"},{"url":"https://api.agentmail.eu"}],"security":[{"BearerAuth":[]},{"TokenAuth":[]}],"x-fern-sdk-group-name":["pods","webhooks"],"x-fern-sdk-method-name":"get"},"patch":{"description":"**CLI:**\n```bash\nagentmail pods webhooks update --pod-id --webhook-id --add-inbox-ids \n```","operationId":"pods_webhooks_update","tags":["PodsWebhooks"],"parameters":[{"name":"pod_id","in":"path","required":true,"schema":{"$ref":"#/components/schemas/podsPodId"}},{"name":"webhook_id","in":"path","required":true,"schema":{"$ref":"#/components/schemas/webhooksWebhookId"}}],"responses":{"200":{"description":"","content":{"application/json":{"schema":{"$ref":"#/components/schemas/webhooksWebhook"}}}},"400":{"description":"","content":{"application/json":{"schema":{"$ref":"#/components/schemas/ValidationErrorResponse"}}}},"404":{"description":"","content":{"application/json":{"schema":{"$ref":"#/components/schemas/ErrorResponse"}}}}},"summary":"Update Webhook","servers":[{"url":"https://api.agentmail.to"},{"url":"https://x402.api.agentmail.to"},{"url":"https://mpp.api.agentmail.to"},{"url":"https://api.agentmail.eu"}],"security":[{"BearerAuth":[]},{"TokenAuth":[]}],"requestBody":{"required":true,"content":{"application/json":{"schema":{"$ref":"#/components/schemas/webhooksUpdatePodWebhookRequest"}}}},"x-fern-sdk-group-name":["pods","webhooks"],"x-fern-sdk-method-name":"update"},"delete":{"description":"**CLI:**\n```bash\nagentmail pods webhooks delete --pod-id --webhook-id \n```","operationId":"pods_webhooks_delete","tags":["PodsWebhooks"],"parameters":[{"name":"pod_id","in":"path","required":true,"schema":{"$ref":"#/components/schemas/podsPodId"}},{"name":"webhook_id","in":"path","required":true,"schema":{"$ref":"#/components/schemas/webhooksWebhookId"}}],"responses":{"204":{"description":""},"404":{"description":"","content":{"application/json":{"schema":{"$ref":"#/components/schemas/ErrorResponse"}}}}},"summary":"Delete Webhook","servers":[{"url":"https://api.agentmail.to"},{"url":"https://x402.api.agentmail.to"},{"url":"https://mpp.api.agentmail.to"},{"url":"https://api.agentmail.eu"}],"security":[{"BearerAuth":[]},{"TokenAuth":[]}],"x-fern-sdk-group-name":["pods","webhooks"],"x-fern-sdk-method-name":"delete"}},"/v0/pods/{pod_id}/webhooks/{webhook_id}/headers":{"get":{"description":"List the names of custom HTTP headers included with deliveries to this pod-scoped webhook.\nHeader values are write-only and are never returned.","operationId":"pods_webhooks_getHeaders","tags":["PodsWebhooks"],"parameters":[{"name":"pod_id","in":"path","required":true,"schema":{"$ref":"#/components/schemas/podsPodId"}},{"name":"webhook_id","in":"path","required":true,"schema":{"$ref":"#/components/schemas/webhooksWebhookId"}}],"responses":{"200":{"description":"","content":{"application/json":{"schema":{"$ref":"#/components/schemas/webhooksWebhookHeaderNamesResponse"}}}},"404":{"description":"","content":{"application/json":{"schema":{"$ref":"#/components/schemas/ErrorResponse"}}}}},"summary":"Get Webhook Headers","servers":[{"url":"https://api.agentmail.to"},{"url":"https://x402.api.agentmail.to"},{"url":"https://mpp.api.agentmail.to"},{"url":"https://api.agentmail.eu"}],"security":[{"BearerAuth":[]},{"TokenAuth":[]}],"x-fern-sdk-group-name":["pods","webhooks"],"x-fern-sdk-method-name":"get-headers"},"patch":{"description":"Atomically set, replace, or remove custom HTTP headers included with deliveries to this\npod-scoped webhook. Header values remain write-only.","operationId":"pods_webhooks_updateHeaders","tags":["PodsWebhooks"],"parameters":[{"name":"pod_id","in":"path","required":true,"schema":{"$ref":"#/components/schemas/podsPodId"}},{"name":"webhook_id","in":"path","required":true,"schema":{"$ref":"#/components/schemas/webhooksWebhookId"}}],"responses":{"204":{"description":""},"400":{"description":"","content":{"application/json":{"schema":{"$ref":"#/components/schemas/ValidationErrorResponse"}}}},"404":{"description":"","content":{"application/json":{"schema":{"$ref":"#/components/schemas/ErrorResponse"}}}}},"summary":"Update Webhook Headers","servers":[{"url":"https://api.agentmail.to"},{"url":"https://x402.api.agentmail.to"},{"url":"https://mpp.api.agentmail.to"},{"url":"https://api.agentmail.eu"}],"security":[{"BearerAuth":[]},{"TokenAuth":[]}],"requestBody":{"required":true,"content":{"application/json":{"schema":{"$ref":"#/components/schemas/webhooksUpdateWebhookHeadersRequest"}}}},"x-fern-sdk-group-name":["pods","webhooks"],"x-fern-sdk-method-name":"update-headers"}},"/v0/threads":{"get":{"description":"Lists threads, most recent first. Pass `senders`, `recipients`, or\n`subject` to filter by substring. Filtered requests are served by\nsearch, which caps `limit` at 100. For relevance-ranked full-text\nsearch across senders, recipients, subject, and message body, use\n`Search Threads`.\n\n**CLI:**\n```bash\nagentmail threads list\n```","operationId":"threads_list","tags":["Threads"],"parameters":[{"name":"limit","in":"query","required":false,"schema":{"$ref":"#/components/schemas/Limit","nullable":true}},{"name":"page_token","in":"query","required":false,"schema":{"$ref":"#/components/schemas/PageToken","nullable":true}},{"name":"labels","in":"query","required":false,"schema":{"$ref":"#/components/schemas/Labels","nullable":true}},{"name":"before","in":"query","required":false,"schema":{"$ref":"#/components/schemas/Before","nullable":true}},{"name":"after","in":"query","required":false,"schema":{"$ref":"#/components/schemas/After","nullable":true}},{"name":"ascending","in":"query","required":false,"schema":{"$ref":"#/components/schemas/Ascending","nullable":true}},{"name":"include_spam","in":"query","required":false,"schema":{"$ref":"#/components/schemas/IncludeSpam","nullable":true}},{"name":"include_blocked","in":"query","required":false,"schema":{"$ref":"#/components/schemas/IncludeBlocked","nullable":true}},{"name":"include_unauthenticated","in":"query","required":false,"schema":{"$ref":"#/components/schemas/IncludeUnauthenticated","nullable":true}},{"name":"include_trash","in":"query","required":false,"schema":{"$ref":"#/components/schemas/IncludeTrash","nullable":true}},{"name":"senders","in":"query","description":"Filter to threads whose senders contain this value (substring match). Repeatable; all values must match.","required":false,"schema":{"type":"array","items":{"type":"string"},"nullable":true}},{"name":"recipients","in":"query","description":"Filter to threads whose recipients contain this value (substring match). Repeatable; all values must match.","required":false,"schema":{"type":"array","items":{"type":"string"},"nullable":true}},{"name":"subject","in":"query","description":"Filter to threads whose subject contains this value (substring match). Repeatable; all values must match.","required":false,"schema":{"type":"array","items":{"type":"string"},"nullable":true}}],"responses":{"200":{"description":"","content":{"application/json":{"schema":{"$ref":"#/components/schemas/ListThreadsResponse"}}}},"404":{"description":"","content":{"application/json":{"schema":{"$ref":"#/components/schemas/ErrorResponse"}}}}},"summary":"List Threads","servers":[{"url":"https://api.agentmail.to"},{"url":"https://x402.api.agentmail.to"},{"url":"https://mpp.api.agentmail.to"},{"url":"https://api.agentmail.eu"}],"security":[{"BearerAuth":[]},{"TokenAuth":[]}],"x-fern-sdk-group-name":["threads"],"x-fern-sdk-method-name":"list"}},"/v0/threads/search":{"get":{"description":"Full-text search across threads in the organization, ranked by\nrelevance. The query is matched against senders, recipients, and\nsubject (substring) and the message body (tokenized full text). Spam,\ntrash, blocked, and unauthenticated threads are always excluded.\n`limit` cannot exceed 100.","operationId":"threads_search","tags":["Threads"],"parameters":[{"name":"q","in":"query","required":true,"schema":{"$ref":"#/components/schemas/Query"}},{"name":"limit","in":"query","required":false,"schema":{"$ref":"#/components/schemas/Limit","nullable":true}},{"name":"page_token","in":"query","required":false,"schema":{"$ref":"#/components/schemas/PageToken","nullable":true}},{"name":"before","in":"query","required":false,"schema":{"$ref":"#/components/schemas/Before","nullable":true}},{"name":"after","in":"query","required":false,"schema":{"$ref":"#/components/schemas/After","nullable":true}}],"responses":{"200":{"description":"","content":{"application/json":{"schema":{"$ref":"#/components/schemas/SearchThreadsResponse"}}}},"400":{"description":"","content":{"application/json":{"schema":{"$ref":"#/components/schemas/ValidationErrorResponse"}}}},"404":{"description":"","content":{"application/json":{"schema":{"$ref":"#/components/schemas/ErrorResponse"}}}}},"summary":"Search Threads","servers":[{"url":"https://api.agentmail.to"},{"url":"https://x402.api.agentmail.to"},{"url":"https://mpp.api.agentmail.to"},{"url":"https://api.agentmail.eu"}],"security":[{"BearerAuth":[]},{"TokenAuth":[]}],"x-fern-sdk-group-name":["threads"],"x-fern-sdk-method-name":"search"}},"/v0/threads/{thread_id}":{"get":{"description":"**CLI:**\n```bash\nagentmail threads get --thread-id \n```","operationId":"threads_get","tags":["Threads"],"parameters":[{"name":"thread_id","in":"path","required":true,"schema":{"$ref":"#/components/schemas/ThreadId"}}],"responses":{"200":{"description":"","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Thread"}}}},"404":{"description":"","content":{"application/json":{"schema":{"$ref":"#/components/schemas/ErrorResponse"}}}}},"summary":"Get Thread","servers":[{"url":"https://api.agentmail.to"},{"url":"https://x402.api.agentmail.to"},{"url":"https://mpp.api.agentmail.to"},{"url":"https://api.agentmail.eu"}],"security":[{"BearerAuth":[]},{"TokenAuth":[]}],"x-fern-sdk-group-name":["threads"],"x-fern-sdk-method-name":"get"},"patch":{"description":"Updates thread labels. Cannot add or remove system labels (sent, received, bounced, etc.). Rejects requests with a `422` for threads with 100 or more messages.","operationId":"threads_update","tags":["Threads"],"parameters":[{"name":"thread_id","in":"path","required":true,"schema":{"$ref":"#/components/schemas/ThreadId"}}],"responses":{"200":{"description":"","content":{"application/json":{"schema":{"$ref":"#/components/schemas/UpdateThreadResponse"}}}},"400":{"description":"","content":{"application/json":{"schema":{"$ref":"#/components/schemas/ValidationErrorResponse"}}}},"404":{"description":"","content":{"application/json":{"schema":{"$ref":"#/components/schemas/ErrorResponse"}}}},"422":{"description":"","content":{"application/json":{"schema":{"$ref":"#/components/schemas/ErrorResponse"}}}}},"summary":"Update Thread","servers":[{"url":"https://api.agentmail.to"},{"url":"https://x402.api.agentmail.to"},{"url":"https://mpp.api.agentmail.to"},{"url":"https://api.agentmail.eu"}],"security":[{"BearerAuth":[]},{"TokenAuth":[]}],"requestBody":{"required":true,"content":{"application/json":{"schema":{"$ref":"#/components/schemas/UpdateThreadRequest"}}}},"x-fern-sdk-group-name":["threads"],"x-fern-sdk-method-name":"update"},"delete":{"description":"Permanently deletes a thread and all of its messages.\n\n**CLI:**\n```bash\nagentmail threads delete --thread-id \n```","operationId":"threads_delete","tags":["Threads"],"parameters":[{"name":"thread_id","in":"path","required":true,"schema":{"$ref":"#/components/schemas/ThreadId"}}],"responses":{"204":{"description":""},"404":{"description":"","content":{"application/json":{"schema":{"$ref":"#/components/schemas/ErrorResponse"}}}}},"summary":"Delete Thread","servers":[{"url":"https://api.agentmail.to"},{"url":"https://x402.api.agentmail.to"},{"url":"https://mpp.api.agentmail.to"},{"url":"https://api.agentmail.eu"}],"security":[{"BearerAuth":[]},{"TokenAuth":[]}],"x-fern-sdk-group-name":["threads"],"x-fern-sdk-method-name":"delete"}},"/v0/threads/{thread_id}/attachments/{attachment_id}":{"get":{"description":"**CLI:**\n```bash\nagentmail threads get-attachment --thread-id --attachment-id \n```","operationId":"threads_getAttachment","tags":["Threads"],"parameters":[{"name":"thread_id","in":"path","required":true,"schema":{"$ref":"#/components/schemas/ThreadId"}},{"name":"attachment_id","in":"path","required":true,"schema":{"$ref":"#/components/schemas/AttachmentId"}}],"responses":{"200":{"description":"","content":{"application/json":{"schema":{"$ref":"#/components/schemas/AttachmentResponse"}}}},"404":{"description":"","content":{"application/json":{"schema":{"$ref":"#/components/schemas/ErrorResponse"}}}}},"summary":"Get Attachment","servers":[{"url":"https://api.agentmail.to"},{"url":"https://x402.api.agentmail.to"},{"url":"https://mpp.api.agentmail.to"},{"url":"https://api.agentmail.eu"}],"security":[{"BearerAuth":[]},{"TokenAuth":[]}],"x-fern-sdk-group-name":["threads"],"x-fern-sdk-method-name":"get-attachment"}}},"components":{"schemas":{"Limit":{"title":"Limit","type":"integer","description":"Limit of number of items returned."},"Count":{"title":"Count","type":"integer","description":"Number of items returned."},"PageToken":{"title":"PageToken","type":"string","description":"Page token for pagination."},"Labels":{"title":"Labels","type":"array","items":{"type":"string"},"description":"Labels to filter by."},"Before":{"title":"Before","type":"string","format":"date-time","description":"Timestamp before which to filter by."},"After":{"title":"After","type":"string","format":"date-time","description":"Timestamp after which to filter by."},"Ascending":{"title":"Ascending","type":"boolean","description":"Sort in ascending temporal order."},"IncludeSpam":{"title":"IncludeSpam","type":"boolean","description":"Include spam in results."},"IncludeBlocked":{"title":"IncludeBlocked","type":"boolean","description":"Include blocked in results."},"IncludeUnauthenticated":{"title":"IncludeUnauthenticated","type":"boolean","description":"Include unauthenticated in results."},"IncludeTrash":{"title":"IncludeTrash","type":"boolean","description":"Include trash in results."},"OrganizationId":{"title":"OrganizationId","type":"string","description":"ID of organization."},"Query":{"title":"Query","type":"string","description":"Full-text search query. Matched against the sender, recipients, and\nsubject (substring) and the message body (tokenized full text)."},"ErrorName":{"title":"ErrorName","type":"string","description":"Name of error."},"ErrorMessage":{"title":"ErrorMessage","type":"string","description":"Error message."},"ErrorCode":{"title":"ErrorCode","type":"string","description":"Stable, machine-readable error code in snake_case (for example, not_found or missing_permission). Branch on this rather than the message text."},"ErrorFix":{"title":"ErrorFix","type":"string","description":"The concrete next action that resolves the error."},"ErrorDocs":{"title":"ErrorDocs","type":"string","description":"Link to the error reference entry for this code."},"ErrorResponse":{"title":"ErrorResponse","type":"object","properties":{"name":{"$ref":"#/components/schemas/ErrorName"},"code":{"$ref":"#/components/schemas/ErrorCode","nullable":true},"message":{"$ref":"#/components/schemas/ErrorMessage"},"fix":{"$ref":"#/components/schemas/ErrorFix","nullable":true},"docs":{"$ref":"#/components/schemas/ErrorDocs","nullable":true}},"required":["name","message"]},"ValidationErrorResponse":{"title":"ValidationErrorResponse","type":"object","properties":{"name":{"$ref":"#/components/schemas/ErrorName"},"code":{"$ref":"#/components/schemas/ErrorCode","nullable":true},"message":{"$ref":"#/components/schemas/ErrorMessage","nullable":true},"errors":{"description":"Validation errors. Each entry has a path and a message identifying the invalid field."},"fix":{"$ref":"#/components/schemas/ErrorFix","nullable":true},"docs":{"$ref":"#/components/schemas/ErrorDocs","nullable":true}},"required":["name","errors"]},"inboxesInboxId":{"title":"inboxesInboxId","type":"string","description":"The ID of the inbox."},"inboxesEmail":{"title":"inboxesEmail","type":"string","description":"Email address of the inbox."},"inboxesDisplayName":{"title":"inboxesDisplayName","type":"string","description":"Display name: `Display Name `."},"inboxesClientId":{"title":"inboxesClientId","type":"string","description":"Client ID of inbox."},"inboxesMetadataValue":{"title":"inboxesMetadataValue","oneOf":[{"type":"string"},{"type":"number","format":"double"},{"type":"boolean"}],"description":"A metadata value. May be a string, number, or boolean."},"inboxesMetadata":{"title":"inboxesMetadata","type":"object","additionalProperties":{"$ref":"#/components/schemas/inboxesMetadataValue"},"description":"Custom key-value pairs attached to the inbox. Up to 256 keys. Keys and\nstring values are each limited to 256 characters. When updating metadata,\nsend a key with a null value to remove that key."},"inboxesUpdateMetadata":{"title":"inboxesUpdateMetadata","type":"object","additionalProperties":{"$ref":"#/components/schemas/inboxesMetadataValue","nullable":true},"description":"Custom key-value pairs to merge into the inbox's existing metadata. A\nvalue may be a string, number, boolean, or null. Setting a key to null\nremoves it. Up to 256 keys; keys and string values are each limited to\n256 characters."},"inboxesInbox":{"title":"inboxesInbox","type":"object","properties":{"pod_id":{"$ref":"#/components/schemas/podsPodId"},"inbox_id":{"$ref":"#/components/schemas/inboxesInboxId"},"email":{"$ref":"#/components/schemas/inboxesEmail"},"display_name":{"$ref":"#/components/schemas/inboxesDisplayName","nullable":true},"client_id":{"$ref":"#/components/schemas/inboxesClientId","nullable":true},"metadata":{"$ref":"#/components/schemas/inboxesMetadata","nullable":true,"description":"Custom metadata attached to the inbox."},"updated_at":{"type":"string","format":"date-time","description":"Time at which inbox was last updated."},"created_at":{"type":"string","format":"date-time","description":"Time at which inbox was created."}},"required":["pod_id","inbox_id","email","updated_at","created_at"]},"inboxesListInboxesResponse":{"title":"inboxesListInboxesResponse","type":"object","properties":{"count":{"$ref":"#/components/schemas/Count"},"limit":{"$ref":"#/components/schemas/Limit","nullable":true},"next_page_token":{"$ref":"#/components/schemas/PageToken","nullable":true},"inboxes":{"type":"array","items":{"$ref":"#/components/schemas/inboxesInbox"},"description":"Ordered by `created_at` descending."}},"required":["count","inboxes"]},"inboxesCreateInboxRequest":{"title":"inboxesCreateInboxRequest","type":"object","properties":{"username":{"type":"string","nullable":true,"description":"Username of address. Randomly generated if not specified."},"domain":{"type":"string","nullable":true,"description":"Domain of address. Must be a verified domain, or any subdomain of a\nverified domain that has subdomains enabled (e.g., `bot.example.com`).\nDefaults to `agentmail.to`."},"display_name":{"$ref":"#/components/schemas/inboxesDisplayName","nullable":true},"client_id":{"$ref":"#/components/schemas/inboxesClientId","nullable":true},"metadata":{"$ref":"#/components/schemas/inboxesMetadata","nullable":true,"description":"Custom metadata to attach to the inbox."}}},"inboxesUpdateInboxRequest":{"title":"inboxesUpdateInboxRequest","type":"object","properties":{"display_name":{"$ref":"#/components/schemas/inboxesDisplayName","nullable":true},"metadata":{"$ref":"#/components/schemas/inboxesUpdateMetadata","nullable":true,"description":"Metadata to merge into the inbox's existing metadata. Keys you include\nare added or overwritten; keys you omit are left unchanged. To remove a\nsingle key, send it with a null value. To clear all metadata, send\n`metadata` as null. Sending an empty object is rejected; use null to\nclear. Each update must include at least one of `display_name` or\n`metadata`."}}},"podsPodId":{"title":"podsPodId","type":"string","description":"ID of pod."},"podsName":{"title":"podsName","type":"string","description":"Name of pod."},"podsClientId":{"title":"podsClientId","type":"string","description":"Client ID of pod."},"podsPod":{"title":"podsPod","type":"object","properties":{"pod_id":{"$ref":"#/components/schemas/podsPodId"},"name":{"$ref":"#/components/schemas/podsName"},"updated_at":{"type":"string","format":"date-time","description":"Time at which pod was last updated."},"created_at":{"type":"string","format":"date-time","description":"Time at which pod was created."},"client_id":{"$ref":"#/components/schemas/podsClientId","nullable":true}},"required":["pod_id","name","updated_at","created_at"]},"podsListPodsResponse":{"title":"podsListPodsResponse","type":"object","properties":{"count":{"$ref":"#/components/schemas/Count"},"limit":{"$ref":"#/components/schemas/Limit","nullable":true},"next_page_token":{"$ref":"#/components/schemas/PageToken","nullable":true},"pods":{"type":"array","items":{"$ref":"#/components/schemas/podsPod"},"description":"Ordered by `created_at` descending."}},"required":["count","pods"]},"podsCreatePodRequest":{"title":"podsCreatePodRequest","type":"object","properties":{"name":{"$ref":"#/components/schemas/podsName","nullable":true},"client_id":{"$ref":"#/components/schemas/podsClientId","nullable":true}}},"webhooksWebhookId":{"title":"webhooksWebhookId","type":"string","description":"ID of webhook."},"webhooksClientId":{"title":"webhooksClientId","type":"string","description":"Client ID of webhook."},"webhooksUrl":{"title":"webhooksUrl","type":"string","description":"URL of webhook endpoint."},"webhooksWebhookHeaders":{"title":"webhooksWebhookHeaders","type":"object","additionalProperties":{"type":"string"},"description":"Custom HTTP headers to include with every delivery to this webhook. Header values are write-only:\nAgentMail never returns them from webhook read endpoints. The map must contain at least one entry\nwhen provided, and every name and value must be a valid HTTP header."},"webhooksWebhookHeaderNamesResponse":{"title":"webhooksWebhookHeaderNamesResponse","type":"object","properties":{"header_names":{"type":"array","items":{"type":"string"},"description":"Names of the custom delivery headers configured for this webhook. Header values are never returned."}},"required":["header_names"]},"webhooksWebhook":{"title":"webhooksWebhook","type":"object","properties":{"webhook_id":{"$ref":"#/components/schemas/webhooksWebhookId"},"url":{"$ref":"#/components/schemas/webhooksUrl"},"event_types":{"$ref":"#/components/schemas/EventTypes","nullable":true},"pod_ids":{"$ref":"#/components/schemas/PodIds","nullable":true},"inbox_ids":{"$ref":"#/components/schemas/InboxIds","nullable":true},"secret":{"type":"string","description":"Secret for webhook signature verification."},"enabled":{"type":"boolean","description":"Webhook is enabled."},"updated_at":{"type":"string","format":"date-time","description":"Time at which webhook was last updated."},"created_at":{"type":"string","format":"date-time","description":"Time at which webhook was created."},"client_id":{"$ref":"#/components/schemas/webhooksClientId","nullable":true}},"required":["webhook_id","url","secret","enabled","updated_at","created_at"]},"webhooksListWebhooksResponse":{"title":"webhooksListWebhooksResponse","type":"object","properties":{"count":{"$ref":"#/components/schemas/Count"},"limit":{"$ref":"#/components/schemas/Limit","nullable":true},"next_page_token":{"$ref":"#/components/schemas/PageToken","nullable":true},"webhooks":{"type":"array","items":{"$ref":"#/components/schemas/webhooksWebhook"},"description":"Ordered by `created_at` descending."}},"required":["count","webhooks"]},"webhooksCreateWebhookEventTypes":{"title":"webhooksCreateWebhookEventTypes","$ref":"#/components/schemas/EventTypes","description":"Full list of event types this webhook should receive. At least one type is required. Send every type you\nwant in this array (not incremental). See [Webhooks overview](https://docs.agentmail.to/webhooks-overview)\nfor spam, blocked, and unauthenticated events and required permissions."},"webhooksUpdateWebhookEventTypes":{"title":"webhooksUpdateWebhookEventTypes","$ref":"#/components/schemas/EventTypes","description":"When you send a non-empty list, it replaces the webhook's subscribed event types in full (the same\n\"set the list\" behavior as create). It is not a merge or diff: include every event type you want after\nthe update. Sending a one-element array means the webhook will only receive that one type afterward.\nOmit this field or send an empty array to leave event types unchanged. Clearing all types with an empty\nlist is not supported. Subscribing to `message.received.spam`, `message.received.blocked`, or\n`message.received.unauthenticated` requires the matching label permission on the API key."},"webhooksCreateInboxWebhookRequest":{"title":"webhooksCreateInboxWebhookRequest","type":"object","description":"Create a webhook scoped to an inbox. The inbox comes from the path, so `inbox_ids` and `pod_ids`\nare not accepted.","properties":{"url":{"$ref":"#/components/schemas/webhooksUrl"},"event_types":{"$ref":"#/components/schemas/webhooksCreateWebhookEventTypes"},"client_id":{"$ref":"#/components/schemas/webhooksClientId","nullable":true},"headers":{"$ref":"#/components/schemas/webhooksWebhookHeaders","nullable":true}},"required":["url","event_types"]},"webhooksCreatePodWebhookRequest":{"title":"webhooksCreatePodWebhookRequest","type":"object","description":"Create a webhook scoped to a pod. The pod comes from the path, so `pod_ids` is not accepted.\nOptionally pass `inbox_ids` to narrow the webhook to specific inboxes within the pod; omit to\nreceive events for the whole pod.","properties":{"inbox_ids":{"$ref":"#/components/schemas/InboxIds","nullable":true}},"allOf":[{"$ref":"#/components/schemas/webhooksCreateInboxWebhookRequest"}]},"webhooksCreateWebhookRequest":{"title":"webhooksCreateWebhookRequest","type":"object","properties":{"pod_ids":{"$ref":"#/components/schemas/PodIds","nullable":true}},"allOf":[{"$ref":"#/components/schemas/webhooksCreatePodWebhookRequest"}]},"webhooksUpdateInboxWebhookRequest":{"title":"webhooksUpdateInboxWebhookRequest","type":"object","description":"Update an inbox-scoped webhook. It is fixed to its inbox, so only `event_types` can change.","properties":{"event_types":{"$ref":"#/components/schemas/webhooksUpdateWebhookEventTypes","nullable":true}}},"webhooksUpdatePodWebhookRequest":{"title":"webhooksUpdatePodWebhookRequest","type":"object","description":"Update a pod-scoped webhook. You can adjust which inboxes within the pod it listens to and replace\nits `event_types`, but not the pod scope itself.","properties":{"add_inbox_ids":{"$ref":"#/components/schemas/InboxIds","nullable":true,"description":"Inbox IDs to subscribe to the webhook."},"remove_inbox_ids":{"$ref":"#/components/schemas/InboxIds","nullable":true,"description":"Inbox IDs to unsubscribe from the webhook."}},"allOf":[{"$ref":"#/components/schemas/webhooksUpdateInboxWebhookRequest"}]},"webhooksUpdateWebhookRequest":{"title":"webhooksUpdateWebhookRequest","type":"object","properties":{"add_pod_ids":{"$ref":"#/components/schemas/PodIds","nullable":true,"description":"Pod IDs to subscribe to the webhook."},"remove_pod_ids":{"$ref":"#/components/schemas/PodIds","nullable":true,"description":"Pod IDs to unsubscribe from the webhook."}},"allOf":[{"$ref":"#/components/schemas/webhooksUpdatePodWebhookRequest"}]},"webhooksUpdateWebhookHeadersRequest":{"title":"webhooksUpdateWebhookHeadersRequest","type":"object","description":"Set, replace, or remove custom delivery headers. Provide at least one of `headers` or\n`remove_headers`. A header cannot be set and removed in the same request, regardless of casing.","properties":{"headers":{"$ref":"#/components/schemas/webhooksWebhookHeaders","nullable":true},"remove_headers":{"type":"array","items":{"type":"string"},"nullable":true,"description":"Names of custom delivery headers to remove."}}},"AgentSignupRequest":{"title":"AgentSignupRequest","type":"object","description":"Request body to sign up an agent.","properties":{"human_email":{"type":"string","description":"Email address of the human who owns the agent. A 6-digit OTP will be sent to this address."},"username":{"type":"string","description":"Username for the auto-created inbox (e.g. \"my-agent\" creates my-agent@agentmail.to)."},"source":{"type":"string","nullable":true,"description":"The SDK, framework, or platform issuing this sign-up (e.g. `agentmail-python`, `agentmail-cli`, `agentmail-mcp`).\nIdentifies the caller — answers \"who is signing up\".\nMax 2048 characters."},"referrer":{"type":"string","nullable":true,"description":"The channel that drove this sign-up — where the agent or its developer discovered AgentMail\n(e.g. `agent.email`, a partner URL, a campaign tag). Answers \"where did this sign-up come from\".\nMax 2048 characters."}},"required":["human_email","username"]},"AgentSignupResponse":{"title":"AgentSignupResponse","type":"object","description":"Response after successful agent sign-up.","properties":{"organization_id":{"type":"string","description":"ID of the created organization."},"inbox_id":{"type":"string","description":"ID of the auto-created inbox."},"api_key":{"type":"string","description":"API key for authenticating subsequent requests. Store this securely, it cannot be retrieved again."}},"required":["organization_id","inbox_id","api_key"]},"AgentVerifyRequest":{"title":"AgentVerifyRequest","type":"object","description":"Request body to verify an agent with an OTP code.","properties":{"otp_code":{"type":"string","description":"6-digit verification code sent to the human's email address."}},"required":["otp_code"]},"AgentVerifyResponse":{"title":"AgentVerifyResponse","type":"object","description":"Response after successful agent verification.","properties":{"verified":{"type":"boolean","description":"Whether the organization was verified."}},"required":["verified"]},"ApiKeyId":{"title":"ApiKeyId","type":"string","description":"ID of api key."},"Prefix":{"title":"Prefix","type":"string","description":"Prefix of api key."},"Name":{"title":"Name","type":"string","description":"Name of api key."},"CreatedAt":{"title":"CreatedAt","type":"string","format":"date-time","description":"Time at which api key was created."},"PublicJwkCoordinate":{"title":"PublicJwkCoordinate","type":"string","pattern":"^[A-Za-z0-9_-]{43}$","minLength":43,"maxLength":43,"description":"A 32-byte P-256 coordinate encoded as unpadded base64url."},"PublicJwk":{"title":"PublicJwk","type":"object","description":"A public P-256 JWK. The object accepts exactly `kty`, `crv`, `x`, and `y`.\nPrivate key material such as `d`, embedded key IDs, and all other members\nare rejected. The server also rejects coordinates that are not a point on\nP-256.","properties":{"kty":{"type":"string","const":"EC"},"crv":{"type":"string","const":"P-256"},"x":{"$ref":"#/components/schemas/PublicJwkCoordinate"},"y":{"$ref":"#/components/schemas/PublicJwkCoordinate"}},"required":["kty","crv","x","y"]},"OrganizationPublicKeyScope":{"title":"OrganizationPublicKeyScope","type":"object","description":"Organization-wide authority.","properties":{}},"PodPublicKeyScope":{"title":"PodPublicKeyScope","type":"object","description":"Authority over one live pod and its inboxes.","properties":{"id":{"type":"string","format":"uuid","description":"ID of the pod."}},"required":["id"]},"InboxPublicKeyScope":{"title":"InboxPublicKeyScope","type":"object","description":"Authority over one live inbox incarnation.","properties":{"id":{"type":"string","format":"email","maxLength":254,"description":"ID of the inbox."}},"required":["id"]},"PublicKeyScope":{"title":"PublicKeyScope","oneOf":[{"type":"object","allOf":[{"type":"object","properties":{"type":{"type":"string","enum":["organization"]}}},{"$ref":"#/components/schemas/OrganizationPublicKeyScope"}],"required":["type"]},{"type":"object","allOf":[{"type":"object","properties":{"type":{"type":"string","enum":["pod"]}}},{"$ref":"#/components/schemas/PodPublicKeyScope"}],"required":["type"]},{"type":"object","allOf":[{"type":"object","properties":{"type":{"type":"string","enum":["inbox"]}}},{"$ref":"#/components/schemas/InboxPublicKeyScope"}],"required":["type"]}],"description":"The immutable scope in which a public-key credential can approve AgentID sign-in."},"PublicKeyMaterial":{"title":"PublicKeyMaterial","type":"object","description":"Registered public key material and its server-computed RFC 7638 thumbprint.","properties":{"jwk":{"$ref":"#/components/schemas/PublicJwk"},"fingerprint":{"type":"string","pattern":"^[A-Za-z0-9_-]{43}$","minLength":43,"maxLength":43,"description":"RFC 7638 SHA-256 JWK thumbprint encoded as unpadded base64url."}},"required":["jwk","fingerprint"]},"PublicKeyCredential":{"title":"PublicKeyCredential","type":"object","description":"An AgentID sign-in credential. `type` and `api_key_id` are server-owned;\nuse `api_key_id` as the JWS `kid`. This response never contains a bearer\nsecret or private key.","properties":{"api_key_id":{"type":"string","format":"uuid","description":"Server-generated credential ID. Store this value as the signing key's `kid`."},"type":{"type":"string","const":"public_key","description":"Server-owned credential discriminator. Callers cannot select or update it."},"name":{"$ref":"#/components/schemas/Name","description":"Human-readable credential name."},"public_key":{"$ref":"#/components/schemas/PublicKeyMaterial"},"scope":{"$ref":"#/components/schemas/PublicKeyScope"},"expires_at":{"type":"string","format":"date-time","nullable":true,"description":"Immutable absolute expiry. Omitted when the credential does not expire."},"revoked_at":{"type":"string","format":"date-time","nullable":true,"description":"Present when organization-wide revoke-all invalidated this credential generation."},"created_at":{"type":"string","format":"date-time"},"updated_at":{"type":"string","format":"date-time"}},"required":["api_key_id","type","name","public_key","scope","created_at","updated_at"]},"CreatePublicKeyRequest":{"title":"CreatePublicKeyRequest","type":"object","description":"Register only a public P-256 JWK. Credential type, `api_key_id`, sign-in\neligibility, permissions, and generation are server-owned and are not\nrequest properties.","properties":{"public_key":{"$ref":"#/components/schemas/PublicJwk"},"name":{"type":"string","minLength":1,"maxLength":256,"nullable":true,"description":"Defaults to `AgentID key {first eight fingerprint characters}`."},"scope":{"$ref":"#/components/schemas/PublicKeyScope","nullable":true,"description":"Omit to inherit the registering bearer key's exact scope. An explicit\nscope must be the caller's scope or a live descendant."},"expires_at":{"type":"string","format":"date-time","nullable":true,"description":"Future absolute expiry. Omit to inherit the registering bearer key's\nexpiry. A child credential cannot outlive its creator."}},"required":["public_key"]},"UpdatePublicKeyNameRequest":{"title":"UpdatePublicKeyNameRequest","type":"object","description":"Rename a public-key credential. Key material, ID, type, scope, sign-in\neligibility, permissions, generation, and expiry are immutable.","properties":{"name":{"type":"string","minLength":1,"maxLength":256}},"required":["name"]},"ListPublicKeysResponse":{"title":"ListPublicKeysResponse","type":"object","properties":{"count":{"$ref":"#/components/schemas/Count"},"next_page_token":{"$ref":"#/components/schemas/PageToken","nullable":true},"public_keys":{"type":"array","items":{"$ref":"#/components/schemas/PublicKeyCredential"},"description":"Public-key credentials only, ordered by creation time descending by default."}},"required":["count","public_keys"]},"RevokeAllAgentIdSignInKeysResponse":{"title":"RevokeAllAgentIdSignInKeysResponse","type":"object","description":"Permanent idempotency receipt for an organization-wide AgentID sign-in key revocation.","properties":{"previous_generation":{"type":"integer","minimum":0},"current_generation":{"type":"integer","minimum":1},"revoked_at":{"type":"string","format":"date-time"}},"required":["previous_generation","current_generation","revoked_at"]},"ApiKeyPermissions":{"title":"ApiKeyPermissions","type":"object","description":"Granular permissions for the API key. When ommitted all permissions are granted. Otherwise, only permissions set to true are granted.","properties":{"inbox_read":{"type":"boolean","nullable":true,"description":"Read inbox details."},"inbox_create":{"type":"boolean","nullable":true,"description":"Create new inboxes."},"inbox_update":{"type":"boolean","nullable":true,"description":"Update inbox settings."},"inbox_delete":{"type":"boolean","nullable":true,"description":"Delete inboxes."},"message_read":{"type":"boolean","nullable":true,"description":"Read messages. Also required to read threads."},"message_send":{"type":"boolean","nullable":true,"description":"Send messages."},"message_update":{"type":"boolean","nullable":true,"description":"Update message labels. Also required to update threads."},"message_delete":{"type":"boolean","nullable":true,"description":"Delete messages. Also required to delete threads."},"label_spam_read":{"type":"boolean","nullable":true,"description":"Access messages labeled spam."},"label_blocked_read":{"type":"boolean","nullable":true,"description":"Access messages labeled blocked."},"label_unauthenticated_read":{"type":"boolean","nullable":true,"description":"Access messages labeled unauthenticated."},"label_trash_read":{"type":"boolean","nullable":true,"description":"Access messages labeled trash."},"draft_read":{"type":"boolean","nullable":true,"description":"Read drafts."},"draft_create":{"type":"boolean","nullable":true,"description":"Create drafts."},"draft_update":{"type":"boolean","nullable":true,"description":"Update drafts."},"draft_delete":{"type":"boolean","nullable":true,"description":"Delete drafts."},"draft_send":{"type":"boolean","nullable":true,"description":"Send drafts."},"webhook_read":{"type":"boolean","nullable":true,"description":"Read webhook configurations."},"webhook_create":{"type":"boolean","nullable":true,"description":"Create webhooks."},"webhook_update":{"type":"boolean","nullable":true,"description":"Update webhooks."},"webhook_delete":{"type":"boolean","nullable":true,"description":"Delete webhooks."},"domain_read":{"type":"boolean","nullable":true,"description":"Read domain details."},"domain_create":{"type":"boolean","nullable":true,"description":"Create domains."},"domain_update":{"type":"boolean","nullable":true,"description":"Update domains."},"domain_delete":{"type":"boolean","nullable":true,"description":"Delete domains."},"list_entry_read":{"type":"boolean","nullable":true,"description":"Read list entries."},"list_entry_create":{"type":"boolean","nullable":true,"description":"Create list entries."},"list_entry_delete":{"type":"boolean","nullable":true,"description":"Delete list entries."},"metrics_read":{"type":"boolean","nullable":true,"description":"Read metrics."},"api_key_read":{"type":"boolean","nullable":true,"description":"Read API keys."},"api_key_create":{"type":"boolean","nullable":true,"description":"Create API keys."},"api_key_update":{"type":"boolean","nullable":true,"description":"Update API keys."},"api_key_delete":{"type":"boolean","nullable":true,"description":"Delete API keys."},"pod_read":{"type":"boolean","nullable":true,"description":"Read pods."},"pod_create":{"type":"boolean","nullable":true,"description":"Create pods."},"pod_delete":{"type":"boolean","nullable":true,"description":"Delete pods."}}},"ApiKey":{"title":"ApiKey","type":"object","properties":{"api_key_id":{"$ref":"#/components/schemas/ApiKeyId"},"prefix":{"$ref":"#/components/schemas/Prefix"},"name":{"$ref":"#/components/schemas/Name"},"pod_id":{"type":"string","nullable":true,"description":"Pod ID the api key is scoped to. If set, the key can only access resources within this pod."},"inbox_id":{"type":"string","nullable":true,"description":"Inbox ID the api key is scoped to. If set, the key can only access resources within this inbox."},"used_at":{"type":"string","format":"date-time","nullable":true,"description":"Time at which api key was last used."},"permissions":{"$ref":"#/components/schemas/ApiKeyPermissions","nullable":true},"created_at":{"$ref":"#/components/schemas/CreatedAt"}},"required":["api_key_id","prefix","name","created_at"]},"CreateApiKeyResponse":{"title":"CreateApiKeyResponse","type":"object","properties":{"api_key_id":{"$ref":"#/components/schemas/ApiKeyId"},"api_key":{"type":"string","description":"API key."},"prefix":{"$ref":"#/components/schemas/Prefix"},"name":{"$ref":"#/components/schemas/Name"},"pod_id":{"type":"string","nullable":true,"description":"Pod ID the api key is scoped to."},"inbox_id":{"type":"string","nullable":true,"description":"Inbox ID the api key is scoped to."},"permissions":{"$ref":"#/components/schemas/ApiKeyPermissions","nullable":true},"created_at":{"$ref":"#/components/schemas/CreatedAt"}},"required":["api_key_id","api_key","prefix","name","created_at"]},"ListApiKeysResponse":{"title":"ListApiKeysResponse","type":"object","properties":{"count":{"$ref":"#/components/schemas/Count"},"next_page_token":{"$ref":"#/components/schemas/PageToken","nullable":true},"api_keys":{"type":"array","items":{"$ref":"#/components/schemas/ApiKey"},"description":"Ordered by `created_at` descending."}},"required":["count","api_keys"]},"CreateApiKeyRequest":{"title":"CreateApiKeyRequest","type":"object","properties":{"name":{"$ref":"#/components/schemas/Name","nullable":true},"permissions":{"$ref":"#/components/schemas/ApiKeyPermissions","nullable":true}}},"AttachmentId":{"title":"AttachmentId","type":"string","description":"ID of attachment."},"AttachmentFilename":{"title":"AttachmentFilename","type":"string","description":"Filename of attachment."},"AttachmentSize":{"title":"AttachmentSize","type":"integer","description":"Size of attachment in bytes."},"AttachmentContentType":{"title":"AttachmentContentType","type":"string","description":"Content type of attachment."},"AttachmentContentDisposition":{"title":"AttachmentContentDisposition","type":"string","enum":["inline","attachment"],"description":"Content disposition of attachment."},"AttachmentContentId":{"title":"AttachmentContentId","type":"string","description":"Content ID of attachment."},"Attachment":{"title":"Attachment","type":"object","properties":{"attachment_id":{"$ref":"#/components/schemas/AttachmentId"},"filename":{"$ref":"#/components/schemas/AttachmentFilename","nullable":true},"size":{"$ref":"#/components/schemas/AttachmentSize"},"content_type":{"$ref":"#/components/schemas/AttachmentContentType","nullable":true},"content_disposition":{"$ref":"#/components/schemas/AttachmentContentDisposition","nullable":true},"content_id":{"$ref":"#/components/schemas/AttachmentContentId","nullable":true}},"required":["attachment_id","size"]},"AttachmentResponse":{"title":"AttachmentResponse","type":"object","properties":{"attachment_id":{"$ref":"#/components/schemas/AttachmentId"},"filename":{"$ref":"#/components/schemas/AttachmentFilename","nullable":true},"size":{"$ref":"#/components/schemas/AttachmentSize"},"content_type":{"$ref":"#/components/schemas/AttachmentContentType","nullable":true},"content_disposition":{"$ref":"#/components/schemas/AttachmentContentDisposition","nullable":true},"content_id":{"$ref":"#/components/schemas/AttachmentContentId","nullable":true},"download_url":{"type":"string","description":"URL to download the attachment."},"expires_at":{"type":"string","format":"date-time","description":"Time at which the download URL expires."}},"required":["attachment_id","size","download_url","expires_at"]},"SendAttachment":{"title":"SendAttachment","type":"object","properties":{"filename":{"$ref":"#/components/schemas/AttachmentFilename","nullable":true},"content_type":{"$ref":"#/components/schemas/AttachmentContentType","nullable":true},"content_disposition":{"$ref":"#/components/schemas/AttachmentContentDisposition","nullable":true},"content_id":{"$ref":"#/components/schemas/AttachmentContentId","nullable":true},"content":{"type":"string","nullable":true,"description":"Base64 encoded content of attachment."},"url":{"type":"string","nullable":true,"description":"URL to the attachment."}}},"ScopeType":{"title":"ScopeType","type":"string","enum":["organization","pod","inbox"],"description":"The scope tier the authenticated credential is bound to."},"Identity":{"title":"Identity","type":"object","description":"Identity and scope of the authenticated credential.","properties":{"scope_type":{"$ref":"#/components/schemas/ScopeType"},"scope_id":{"type":"string","description":"ID of the most specific scope the credential is bound to.\nEquals inbox_id when scope_type is inbox, pod_id when pod, organization_id when organization."},"organization_id":{"$ref":"#/components/schemas/OrganizationId"},"pod_id":{"type":"string","nullable":true,"description":"ID of the pod the credential is scoped to. Present when scope_type is pod or inbox."},"inbox_id":{"type":"string","nullable":true,"description":"ID of the inbox the credential is scoped to. Present when scope_type is inbox."},"api_key_id":{"type":"string","nullable":true,"description":"ID of the API key used to authenticate. Absent for JWT and proxy credentials."}},"required":["scope_type","scope_id","organization_id"]},"DomainId":{"title":"DomainId","type":"string","description":"The ID of the domain."},"DomainName":{"title":"DomainName","type":"string","description":"The name of the domain (e.g., `example.com`)."},"RecordType":{"title":"RecordType","type":"string","enum":["TXT","CNAME","MX"]},"VerificationStatus":{"title":"VerificationStatus","type":"string","enum":["NOT_STARTED","PENDING","INVALID","FAILED","VERIFYING","VERIFIED"]},"RecordStatus":{"title":"RecordStatus","type":"string","enum":["MISSING","INVALID","VALID"]},"VerificationRecord":{"title":"VerificationRecord","type":"object","properties":{"type":{"$ref":"#/components/schemas/RecordType","description":"The type of the DNS record."},"name":{"type":"string","description":"The name or host of the record."},"value":{"type":"string","description":"The value of the record."},"status":{"$ref":"#/components/schemas/RecordStatus","description":"The verification status of this specific record."},"priority":{"type":"integer","nullable":true,"description":"The priority of the MX record."}},"required":["type","name","value","status"]},"Status":{"title":"Status","$ref":"#/components/schemas/VerificationStatus","description":"The verification status of the domain."},"FeedbackEnabled":{"title":"FeedbackEnabled","type":"boolean","description":"Bounce and complaint notifications are sent to your inboxes."},"SubdomainsEnabled":{"title":"SubdomainsEnabled","type":"boolean","description":"Allow inboxes on any subdomain of this domain. Adds a required wildcard MX\nrecord (`*.`) to `records`."},"TrackingEnabled":{"title":"TrackingEnabled","type":"boolean","description":"Serve open tracking pixels from this domain. Adds a required `link.`\nCNAME record to `records`, which must be published and verified before\n`track_opens` can be used on a send."},"ClientId":{"title":"ClientId","type":"string","description":"Client ID of domain."},"Domain":{"title":"Domain","type":"object","properties":{"pod_id":{"$ref":"#/components/schemas/podsPodId","nullable":true},"domain_id":{"$ref":"#/components/schemas/DomainId"},"domain":{"$ref":"#/components/schemas/DomainName"},"status":{"$ref":"#/components/schemas/Status"},"feedback_enabled":{"$ref":"#/components/schemas/FeedbackEnabled"},"subdomains_enabled":{"$ref":"#/components/schemas/SubdomainsEnabled"},"tracking_enabled":{"$ref":"#/components/schemas/TrackingEnabled"},"records":{"type":"array","items":{"$ref":"#/components/schemas/VerificationRecord"},"description":"A list of DNS records required to verify the domain. Includes a\nwildcard MX record (`*.`) when `subdomains_enabled` is true."},"client_id":{"$ref":"#/components/schemas/ClientId","nullable":true},"updated_at":{"type":"string","format":"date-time","description":"Time at which the domain was last updated."},"created_at":{"type":"string","format":"date-time","description":"Time at which the domain was created."}},"required":["domain_id","domain","status","feedback_enabled","subdomains_enabled","tracking_enabled","records","updated_at","created_at"]},"DomainItem":{"title":"DomainItem","type":"object","properties":{"pod_id":{"$ref":"#/components/schemas/podsPodId","nullable":true},"domain_id":{"$ref":"#/components/schemas/DomainId"},"domain":{"$ref":"#/components/schemas/DomainName"},"feedback_enabled":{"$ref":"#/components/schemas/FeedbackEnabled"},"subdomains_enabled":{"$ref":"#/components/schemas/SubdomainsEnabled"},"tracking_enabled":{"$ref":"#/components/schemas/TrackingEnabled"},"client_id":{"$ref":"#/components/schemas/ClientId","nullable":true},"updated_at":{"type":"string","format":"date-time","description":"Time at which the domain was last updated."},"created_at":{"type":"string","format":"date-time","description":"Time at which the domain was created."}},"required":["domain_id","domain","feedback_enabled","subdomains_enabled","tracking_enabled","updated_at","created_at"]},"ListDomainsResponse":{"title":"ListDomainsResponse","type":"object","properties":{"count":{"$ref":"#/components/schemas/Count"},"limit":{"$ref":"#/components/schemas/Limit","nullable":true},"next_page_token":{"$ref":"#/components/schemas/PageToken","nullable":true},"domains":{"type":"array","items":{"$ref":"#/components/schemas/DomainItem"},"description":"Ordered by `created_at` descending."}},"required":["count","domains"]},"CreateDomainRequest":{"title":"CreateDomainRequest","type":"object","properties":{"domain":{"$ref":"#/components/schemas/DomainName"},"feedback_enabled":{"$ref":"#/components/schemas/FeedbackEnabled","nullable":true},"subdomains_enabled":{"$ref":"#/components/schemas/SubdomainsEnabled","nullable":true},"tracking_enabled":{"$ref":"#/components/schemas/TrackingEnabled","nullable":true}},"required":["domain"]},"UpdateDomainRequest":{"title":"UpdateDomainRequest","type":"object","description":"Provide at least one of `feedback_enabled`, `subdomains_enabled`, or\n`tracking_enabled`. Omitted\nfields are left unchanged; an empty body is rejected. Enabling\n`subdomains_enabled` on a verified domain returns it to `PENDING` until the\nnewly-required wildcard MX record (`*.`) is published and verified.","properties":{"feedback_enabled":{"$ref":"#/components/schemas/FeedbackEnabled","nullable":true},"subdomains_enabled":{"$ref":"#/components/schemas/SubdomainsEnabled","nullable":true},"tracking_enabled":{"$ref":"#/components/schemas/TrackingEnabled","nullable":true}}},"DraftId":{"title":"DraftId","type":"string","description":"ID of draft."},"DraftClientId":{"title":"DraftClientId","type":"string","description":"Client ID of draft."},"DraftLabels":{"title":"DraftLabels","type":"array","items":{"type":"string"},"description":"Labels of draft."},"DraftReplyTo":{"title":"DraftReplyTo","type":"array","items":{"type":"string"},"description":"Reply-to addresses. In format `username@domain.com` or `Display Name `."},"DraftTo":{"title":"DraftTo","type":"array","items":{"type":"string"},"description":"Addresses of recipients. In format `username@domain.com` or `Display Name `."},"DraftCc":{"title":"DraftCc","type":"array","items":{"type":"string"},"description":"Addresses of CC recipients. In format `username@domain.com` or `Display Name `."},"DraftBcc":{"title":"DraftBcc","type":"array","items":{"type":"string"},"description":"Addresses of BCC recipients. In format `username@domain.com` or `Display Name `."},"DraftSubject":{"title":"DraftSubject","type":"string","description":"Subject of draft."},"DraftPreview":{"title":"DraftPreview","type":"string","description":"Text preview of draft."},"DraftText":{"title":"DraftText","type":"string","description":"Plain text body of draft."},"DraftHtml":{"title":"DraftHtml","type":"string","description":"HTML body of draft."},"DraftAttachments":{"title":"DraftAttachments","type":"array","items":{"$ref":"#/components/schemas/Attachment"},"description":"Attachments in draft."},"DraftInReplyTo":{"title":"DraftInReplyTo","type":"string","description":"ID of message being replied to."},"DraftForwardOf":{"title":"DraftForwardOf","type":"string","description":"ID of message being forwarded."},"DraftReplyAll":{"title":"DraftReplyAll","type":"boolean","description":"Reply to all recipients of the original message."},"DraftSendStatus":{"title":"DraftSendStatus","type":"string","enum":["scheduled","sending","failed"],"description":"Schedule send status of draft."},"DraftSendAt":{"title":"DraftSendAt","type":"string","format":"date-time","description":"Time at which to schedule send draft."},"DraftUpdatedAt":{"title":"DraftUpdatedAt","type":"string","format":"date-time","description":"Time at which draft was last updated."},"DraftItem":{"title":"DraftItem","type":"object","properties":{"inbox_id":{"$ref":"#/components/schemas/inboxesInboxId"},"draft_id":{"$ref":"#/components/schemas/DraftId"},"labels":{"$ref":"#/components/schemas/DraftLabels"},"to":{"$ref":"#/components/schemas/DraftTo","nullable":true},"cc":{"$ref":"#/components/schemas/DraftCc","nullable":true},"bcc":{"$ref":"#/components/schemas/DraftBcc","nullable":true},"subject":{"$ref":"#/components/schemas/DraftSubject","nullable":true},"preview":{"$ref":"#/components/schemas/DraftPreview","nullable":true},"attachments":{"$ref":"#/components/schemas/DraftAttachments","nullable":true},"in_reply_to":{"$ref":"#/components/schemas/DraftInReplyTo","nullable":true},"forward_of":{"$ref":"#/components/schemas/DraftForwardOf","nullable":true},"send_status":{"$ref":"#/components/schemas/DraftSendStatus","nullable":true},"send_at":{"$ref":"#/components/schemas/DraftSendAt","nullable":true},"updated_at":{"$ref":"#/components/schemas/DraftUpdatedAt"}},"required":["inbox_id","draft_id","labels","updated_at"]},"Draft":{"title":"Draft","type":"object","properties":{"inbox_id":{"$ref":"#/components/schemas/inboxesInboxId"},"draft_id":{"$ref":"#/components/schemas/DraftId"},"client_id":{"$ref":"#/components/schemas/DraftClientId","nullable":true},"labels":{"$ref":"#/components/schemas/DraftLabels"},"reply_to":{"$ref":"#/components/schemas/DraftReplyTo","nullable":true},"to":{"$ref":"#/components/schemas/DraftTo","nullable":true},"cc":{"$ref":"#/components/schemas/DraftCc","nullable":true},"bcc":{"$ref":"#/components/schemas/DraftBcc","nullable":true},"subject":{"$ref":"#/components/schemas/DraftSubject","nullable":true},"preview":{"$ref":"#/components/schemas/DraftPreview","nullable":true},"text":{"$ref":"#/components/schemas/DraftText","nullable":true},"html":{"$ref":"#/components/schemas/DraftHtml","nullable":true},"attachments":{"$ref":"#/components/schemas/DraftAttachments","nullable":true},"in_reply_to":{"$ref":"#/components/schemas/DraftInReplyTo","nullable":true},"forward_of":{"$ref":"#/components/schemas/DraftForwardOf","nullable":true},"references":{"type":"array","items":{"type":"string"},"nullable":true,"description":"IDs of previous messages in thread."},"send_status":{"$ref":"#/components/schemas/DraftSendStatus","nullable":true},"send_at":{"$ref":"#/components/schemas/DraftSendAt","nullable":true},"updated_at":{"$ref":"#/components/schemas/DraftUpdatedAt"},"created_at":{"type":"string","format":"date-time","description":"Time at which draft was created."}},"required":["inbox_id","draft_id","labels","updated_at","created_at"]},"ListDraftsResponse":{"title":"ListDraftsResponse","type":"object","properties":{"count":{"$ref":"#/components/schemas/Count"},"limit":{"$ref":"#/components/schemas/Limit","nullable":true},"next_page_token":{"$ref":"#/components/schemas/PageToken","nullable":true},"drafts":{"type":"array","items":{"$ref":"#/components/schemas/DraftItem"},"description":"Ordered by `updated_at` descending."}},"required":["count","drafts"]},"CreateDraftRequest":{"title":"CreateDraftRequest","type":"object","description":"Body for creating a draft. Supports plain, reply, reply-all, and forward\ndrafts:\n\n- **Plain draft:** supply `to`, `subject`, `text`, etc.\n- **Reply:** set `in_reply_to` to a message ID. Recipients, subject, and\n threading are derived from that message. Set `reply_all` to address the\n whole thread (you then cannot also pass `to`, `cc`, or `bcc`).\n- **Forward:** set `forward_of` to a message ID. The subject and threading\n are derived from the source message, whose body and attachments are\n merged in at send time.\n\n`in_reply_to` and `forward_of` are mutually exclusive, and reading the\nreferenced message requires `message_read` permission.","properties":{"labels":{"$ref":"#/components/schemas/DraftLabels","nullable":true},"reply_to":{"$ref":"#/components/schemas/DraftReplyTo","nullable":true},"to":{"$ref":"#/components/schemas/DraftTo","nullable":true},"cc":{"$ref":"#/components/schemas/DraftCc","nullable":true},"bcc":{"$ref":"#/components/schemas/DraftBcc","nullable":true},"subject":{"$ref":"#/components/schemas/DraftSubject","nullable":true},"text":{"$ref":"#/components/schemas/DraftText","nullable":true},"html":{"$ref":"#/components/schemas/DraftHtml","nullable":true},"attachments":{"type":"array","items":{"$ref":"#/components/schemas/SendAttachment"},"nullable":true,"description":"Attachments to include in draft."},"in_reply_to":{"$ref":"#/components/schemas/DraftInReplyTo","nullable":true},"forward_of":{"$ref":"#/components/schemas/DraftForwardOf","nullable":true},"reply_all":{"$ref":"#/components/schemas/DraftReplyAll","nullable":true},"send_at":{"$ref":"#/components/schemas/DraftSendAt","nullable":true},"client_id":{"$ref":"#/components/schemas/DraftClientId","nullable":true}}},"UpdateDraftRequest":{"title":"UpdateDraftRequest","type":"object","description":"Edit fields on an existing draft. A draft's kind (plain, reply, or forward)\nis fixed at creation and cannot be changed here. Omitting a field leaves it\nunchanged; passing `null` (or `[]` for a recipient field) clears it. Pass\n`send_at` to schedule or reschedule the draft, or `null` to un-schedule it.","properties":{"reply_to":{"$ref":"#/components/schemas/DraftReplyTo","nullable":true},"to":{"$ref":"#/components/schemas/DraftTo","nullable":true},"cc":{"$ref":"#/components/schemas/DraftCc","nullable":true},"bcc":{"$ref":"#/components/schemas/DraftBcc","nullable":true},"subject":{"$ref":"#/components/schemas/DraftSubject","nullable":true},"text":{"$ref":"#/components/schemas/DraftText","nullable":true},"html":{"$ref":"#/components/schemas/DraftHtml","nullable":true},"add_attachments":{"type":"array","items":{"$ref":"#/components/schemas/SendAttachment"},"nullable":true,"description":"Attachments to add to the draft."},"remove_attachments":{"type":"array","items":{"$ref":"#/components/schemas/AttachmentId"},"nullable":true,"description":"IDs of attachments to remove from the draft."},"add_labels":{"$ref":"#/components/schemas/DraftLabels","nullable":true,"description":"Label or labels to add to the draft."},"remove_labels":{"$ref":"#/components/schemas/DraftLabels","nullable":true,"description":"Label or labels to remove from the draft."},"send_at":{"$ref":"#/components/schemas/DraftSendAt","nullable":true}}},"EventType":{"title":"EventType","type":"string","enum":["message.received","message.received.spam","message.received.blocked","message.received.unauthenticated","message.sent","message.delivered","message.bounced","message.complained","message.rejected","message.opened","domain.verified"]},"EventTypes":{"title":"EventTypes","type":"array","items":{"$ref":"#/components/schemas/EventType"},"description":"Event types for which to send events."},"MessageReceivedEventType":{"title":"MessageReceivedEventType","type":"string","enum":["message.received","message.received.spam","message.received.blocked","message.received.unauthenticated"]},"PodIds":{"title":"PodIds","type":"array","items":{"type":"string"},"description":"Pods for which to send events. Maximum 10 per webhook."},"InboxIds":{"title":"InboxIds","type":"array","items":{"type":"string"},"description":"Inboxes for which to send events. Maximum 10 per webhook."},"EventId":{"title":"EventId","type":"string","description":"ID of event."},"Timestamp":{"title":"Timestamp","type":"string","format":"date-time","description":"Timestamp of event."},"Recipient":{"title":"Recipient","type":"object","properties":{"address":{"type":"string","description":"Recipient address."},"status":{"type":"string","description":"Recipient status."}},"required":["address","status"]},"Send":{"title":"Send","type":"object","properties":{"inbox_id":{"$ref":"#/components/schemas/inboxesInboxId"},"thread_id":{"$ref":"#/components/schemas/ThreadId"},"message_id":{"$ref":"#/components/schemas/MessageId"},"timestamp":{"$ref":"#/components/schemas/Timestamp"},"recipients":{"type":"array","items":{"type":"string"},"description":"Sent recipients."}},"required":["inbox_id","thread_id","message_id","timestamp","recipients"],"x-fern-type-name":"SendEvent"},"Delivery":{"title":"Delivery","type":"object","properties":{"inbox_id":{"$ref":"#/components/schemas/inboxesInboxId"},"thread_id":{"$ref":"#/components/schemas/ThreadId"},"message_id":{"$ref":"#/components/schemas/MessageId"},"timestamp":{"$ref":"#/components/schemas/Timestamp"},"recipients":{"type":"array","items":{"type":"string"},"description":"Delivered recipients."}},"required":["inbox_id","thread_id","message_id","timestamp","recipients"]},"Bounce":{"title":"Bounce","type":"object","properties":{"inbox_id":{"$ref":"#/components/schemas/inboxesInboxId"},"thread_id":{"$ref":"#/components/schemas/ThreadId"},"message_id":{"$ref":"#/components/schemas/MessageId"},"timestamp":{"$ref":"#/components/schemas/Timestamp"},"type":{"type":"string","description":"Bounce type."},"sub_type":{"type":"string","description":"Bounce sub-type."},"recipients":{"type":"array","items":{"$ref":"#/components/schemas/Recipient"},"description":"Bounced recipients."}},"required":["inbox_id","thread_id","message_id","timestamp","type","sub_type","recipients"]},"Complaint":{"title":"Complaint","type":"object","properties":{"inbox_id":{"$ref":"#/components/schemas/inboxesInboxId"},"thread_id":{"$ref":"#/components/schemas/ThreadId"},"message_id":{"$ref":"#/components/schemas/MessageId"},"timestamp":{"$ref":"#/components/schemas/Timestamp"},"type":{"type":"string","description":"Complaint type."},"sub_type":{"type":"string","description":"Complaint sub-type."},"recipients":{"type":"array","items":{"type":"string"},"description":"Complained recipients."}},"required":["inbox_id","thread_id","message_id","timestamp","type","sub_type","recipients"]},"Reject":{"title":"Reject","type":"object","properties":{"inbox_id":{"$ref":"#/components/schemas/inboxesInboxId"},"thread_id":{"$ref":"#/components/schemas/ThreadId"},"message_id":{"$ref":"#/components/schemas/MessageId"},"timestamp":{"$ref":"#/components/schemas/Timestamp"},"reason":{"type":"string","description":"Reject reason."}},"required":["inbox_id","thread_id","message_id","timestamp","reason"]},"Open":{"title":"Open","type":"object","properties":{"inbox_id":{"$ref":"#/components/schemas/inboxesInboxId"},"thread_id":{"$ref":"#/components/schemas/ThreadId"},"message_id":{"$ref":"#/components/schemas/MessageId"},"timestamp":{"$ref":"#/components/schemas/Timestamp"}},"required":["inbox_id","thread_id","message_id","timestamp"]},"MessageReceivedEvent":{"title":"MessageReceivedEvent","type":"object","description":"A message was received. Spam, blocked, and unauthenticated received-message events use the same payload shape with different `event_type` values.","properties":{"type":{"type":"string","const":"event"},"event_type":{"$ref":"#/components/schemas/MessageReceivedEventType"},"event_id":{"$ref":"#/components/schemas/EventId"},"message":{"$ref":"#/components/schemas/Message"},"thread":{"$ref":"#/components/schemas/ThreadItem"}},"required":["type","event_type","event_id","message","thread"]},"MessageSentEvent":{"title":"MessageSentEvent","type":"object","properties":{"type":{"type":"string","const":"event"},"event_type":{"type":"string","const":"message.sent"},"event_id":{"$ref":"#/components/schemas/EventId"},"send":{"$ref":"#/components/schemas/Send"}},"required":["type","event_type","event_id","send"]},"MessageDeliveredEvent":{"title":"MessageDeliveredEvent","type":"object","properties":{"type":{"type":"string","const":"event"},"event_type":{"type":"string","const":"message.delivered"},"event_id":{"$ref":"#/components/schemas/EventId"},"delivery":{"$ref":"#/components/schemas/Delivery"}},"required":["type","event_type","event_id","delivery"]},"MessageBouncedEvent":{"title":"MessageBouncedEvent","type":"object","properties":{"type":{"type":"string","const":"event"},"event_type":{"type":"string","const":"message.bounced"},"event_id":{"$ref":"#/components/schemas/EventId"},"bounce":{"$ref":"#/components/schemas/Bounce"}},"required":["type","event_type","event_id","bounce"]},"MessageComplainedEvent":{"title":"MessageComplainedEvent","type":"object","properties":{"type":{"type":"string","const":"event"},"event_type":{"type":"string","const":"message.complained"},"event_id":{"$ref":"#/components/schemas/EventId"},"complaint":{"$ref":"#/components/schemas/Complaint"}},"required":["type","event_type","event_id","complaint"]},"MessageRejectedEvent":{"title":"MessageRejectedEvent","type":"object","properties":{"type":{"type":"string","const":"event"},"event_type":{"type":"string","const":"message.rejected"},"event_id":{"$ref":"#/components/schemas/EventId"},"reject":{"$ref":"#/components/schemas/Reject"}},"required":["type","event_type","event_id","reject"]},"MessageOpenedEvent":{"title":"MessageOpenedEvent","type":"object","description":"A tracked message was opened for the first time. Sent once per message: repeat opens do not\nresend it.","properties":{"type":{"type":"string","const":"event"},"event_type":{"type":"string","const":"message.opened"},"event_id":{"$ref":"#/components/schemas/EventId"},"open":{"$ref":"#/components/schemas/Open"}},"required":["type","event_type","event_id","open"]},"DomainVerifiedEvent":{"title":"DomainVerifiedEvent","type":"object","properties":{"type":{"type":"string","const":"event"},"event_type":{"type":"string","const":"domain.verified"},"event_id":{"$ref":"#/components/schemas/EventId"},"domain":{"$ref":"#/components/schemas/Domain"}},"required":["type","event_type","event_id","domain"]},"InboxEventId":{"title":"InboxEventId","type":"string","description":"ID of event."},"InboxEventType":{"title":"InboxEventType","type":"string","enum":["label.added","label.removed"],"description":"Type of inbox event. Wire format is dot.case to match the\nconvention used by webhook events (`message.received`,\n`domain.verified`, etc. in events.yml). Pre-2026-04 these were\n`label_added`/`label_removed` (snake_case). The Fern enum's `name`\nfield stays uppercase-snake (Fern convention); only the wire\n`value` changed."},"InboxEvent":{"title":"InboxEvent","type":"object","properties":{"organization_id":{"$ref":"#/components/schemas/OrganizationId"},"pod_id":{"type":"string","description":"ID of pod."},"inbox_id":{"$ref":"#/components/schemas/inboxesInboxId"},"event_id":{"$ref":"#/components/schemas/InboxEventId"},"event_type":{"$ref":"#/components/schemas/InboxEventType"},"message_id":{"type":"string","description":"ID of message."},"label":{"type":"string","description":"Label added or removed."},"event_at":{"type":"string","format":"date-time","description":"Time at which the event occurred."},"created_at":{"type":"string","format":"date-time","description":"Time at which the event was recorded."}},"required":["organization_id","pod_id","inbox_id","event_id","event_type","message_id","label","event_at","created_at"]},"ListInboxEventsResponse":{"title":"ListInboxEventsResponse","type":"object","properties":{"count":{"$ref":"#/components/schemas/Count"},"limit":{"$ref":"#/components/schemas/Limit","nullable":true},"next_page_token":{"$ref":"#/components/schemas/PageToken","nullable":true},"events":{"type":"array","items":{"$ref":"#/components/schemas/InboxEvent"},"description":"Ordered by `event_id` descending."}},"required":["count","events"]},"Direction":{"title":"Direction","type":"string","enum":["send","receive","reply"],"description":"Direction of list entry."},"ListType":{"title":"ListType","type":"string","enum":["allow","block"],"description":"Type of list entry."},"EntryType":{"title":"EntryType","type":"string","enum":["email","domain"],"description":"Whether the entry is an email address or domain."},"ListEntryBase":{"title":"ListEntryBase","type":"object","properties":{"entry":{"type":"string","description":"Email address or domain of list entry."},"organization_id":{"$ref":"#/components/schemas/OrganizationId"},"reason":{"type":"string","nullable":true,"description":"Reason for adding the entry."},"direction":{"$ref":"#/components/schemas/Direction"},"list_type":{"$ref":"#/components/schemas/ListType"},"entry_type":{"$ref":"#/components/schemas/EntryType"},"created_at":{"type":"string","format":"date-time","description":"Time at which entry was created."},"read_only":{"type":"boolean","nullable":true,"description":"Whether the entry is read-only and cannot be deleted via the API."}},"required":["entry","organization_id","direction","list_type","entry_type","created_at"]},"ListEntry":{"title":"ListEntry","type":"object","properties":{},"allOf":[{"$ref":"#/components/schemas/ListEntryBase"}]},"PodListEntry":{"title":"PodListEntry","type":"object","properties":{"pod_id":{"type":"string","description":"ID of pod."},"inbox_id":{"type":"string","nullable":true,"description":"ID of inbox, if entry is inbox-scoped."}},"required":["pod_id"],"allOf":[{"$ref":"#/components/schemas/ListEntryBase"}]},"PodListListEntriesResponse":{"title":"PodListListEntriesResponse","type":"object","properties":{"count":{"$ref":"#/components/schemas/Count"},"limit":{"$ref":"#/components/schemas/Limit","nullable":true},"next_page_token":{"$ref":"#/components/schemas/PageToken","nullable":true},"entries":{"type":"array","items":{"$ref":"#/components/schemas/PodListEntry"},"description":"Ordered by entry ascending."}},"required":["count","entries"]},"ListListEntriesResponse":{"title":"ListListEntriesResponse","type":"object","properties":{"count":{"$ref":"#/components/schemas/Count"},"limit":{"$ref":"#/components/schemas/Limit","nullable":true},"next_page_token":{"$ref":"#/components/schemas/PageToken","nullable":true},"entries":{"type":"array","items":{"$ref":"#/components/schemas/ListEntry"},"description":"Ordered by entry ascending."}},"required":["count","entries"]},"CreateListEntryRequest":{"title":"CreateListEntryRequest","type":"object","properties":{"entry":{"type":"string","description":"Email address or domain to add."},"reason":{"type":"string","nullable":true,"description":"Reason for adding the entry."}},"required":["entry"]},"MessageId":{"title":"MessageId","type":"string","description":"ID of message."},"MessageLabels":{"title":"MessageLabels","type":"array","items":{"type":"string"},"description":"Labels of message."},"MessageTimestamp":{"title":"MessageTimestamp","type":"string","format":"date-time","description":"Time at which message was sent or drafted."},"MessageFrom":{"title":"MessageFrom","type":"string","description":"Address of sender. In format `username@domain.com` or `Display Name `."},"MessageReplyTo":{"title":"MessageReplyTo","type":"array","items":{"type":"string"},"description":"Addresses of reply-to recipients. In format `username@domain.com` or `Display Name `."},"MessageTo":{"title":"MessageTo","type":"array","items":{"type":"string"},"description":"Addresses of recipients. In format `username@domain.com` or `Display Name `."},"MessageCc":{"title":"MessageCc","type":"array","items":{"type":"string"},"description":"Addresses of CC recipients. In format `username@domain.com` or `Display Name `."},"MessageBcc":{"title":"MessageBcc","type":"array","items":{"type":"string"},"description":"Addresses of BCC recipients. In format `username@domain.com` or `Display Name `."},"MessageSubject":{"title":"MessageSubject","type":"string","description":"Subject of message."},"MessagePreview":{"title":"MessagePreview","type":"string","description":"Text preview of message."},"MessageText":{"title":"MessageText","type":"string","description":"Plain text body of message."},"MessageHtml":{"title":"MessageHtml","type":"string","description":"HTML body of message."},"MessageAttachments":{"title":"MessageAttachments","type":"array","items":{"$ref":"#/components/schemas/Attachment"},"description":"Attachments in message."},"MessageInReplyTo":{"title":"MessageInReplyTo","type":"string","description":"ID of message being replied to."},"MessageReferences":{"title":"MessageReferences","type":"array","items":{"type":"string"},"description":"IDs of previous messages in thread."},"MessageHeaders":{"title":"MessageHeaders","type":"object","additionalProperties":{"type":"string"},"description":"Headers in message."},"MessageSize":{"title":"MessageSize","type":"integer","description":"Size of message in bytes."},"MessageUpdatedAt":{"title":"MessageUpdatedAt","type":"string","format":"date-time","description":"Time at which message was last updated."},"MessageCreatedAt":{"title":"MessageCreatedAt","type":"string","format":"date-time","description":"Time at which message was created."},"MessageItem":{"title":"MessageItem","type":"object","properties":{"inbox_id":{"$ref":"#/components/schemas/inboxesInboxId"},"thread_id":{"$ref":"#/components/schemas/ThreadId"},"message_id":{"$ref":"#/components/schemas/MessageId"},"labels":{"$ref":"#/components/schemas/MessageLabels"},"timestamp":{"$ref":"#/components/schemas/MessageTimestamp"},"from":{"$ref":"#/components/schemas/MessageFrom"},"to":{"$ref":"#/components/schemas/MessageTo"},"cc":{"$ref":"#/components/schemas/MessageCc","nullable":true},"bcc":{"$ref":"#/components/schemas/MessageBcc","nullable":true},"subject":{"$ref":"#/components/schemas/MessageSubject","nullable":true},"preview":{"$ref":"#/components/schemas/MessagePreview","nullable":true},"attachments":{"$ref":"#/components/schemas/MessageAttachments","nullable":true},"in_reply_to":{"$ref":"#/components/schemas/MessageInReplyTo","nullable":true},"references":{"$ref":"#/components/schemas/MessageReferences","nullable":true},"headers":{"$ref":"#/components/schemas/MessageHeaders","nullable":true},"size":{"$ref":"#/components/schemas/MessageSize"},"updated_at":{"$ref":"#/components/schemas/MessageUpdatedAt"},"created_at":{"$ref":"#/components/schemas/MessageCreatedAt"}},"required":["inbox_id","thread_id","message_id","labels","timestamp","from","to","size","updated_at","created_at"]},"Message":{"title":"Message","type":"object","properties":{"inbox_id":{"$ref":"#/components/schemas/inboxesInboxId"},"thread_id":{"$ref":"#/components/schemas/ThreadId"},"message_id":{"$ref":"#/components/schemas/MessageId"},"labels":{"$ref":"#/components/schemas/MessageLabels"},"timestamp":{"$ref":"#/components/schemas/MessageTimestamp"},"from":{"$ref":"#/components/schemas/MessageFrom"},"reply_to":{"type":"array","items":{"type":"string"},"nullable":true,"description":"Reply-to addresses. In format `username@domain.com` or `Display Name `."},"to":{"$ref":"#/components/schemas/MessageTo"},"cc":{"$ref":"#/components/schemas/MessageCc","nullable":true},"bcc":{"$ref":"#/components/schemas/MessageBcc","nullable":true},"subject":{"$ref":"#/components/schemas/MessageSubject","nullable":true},"preview":{"$ref":"#/components/schemas/MessagePreview","nullable":true},"text":{"$ref":"#/components/schemas/MessageText","nullable":true},"html":{"$ref":"#/components/schemas/MessageHtml","nullable":true},"extracted_text":{"type":"string","nullable":true,"description":"Extracted new text content."},"extracted_html":{"type":"string","nullable":true,"description":"Extracted new HTML content."},"attachments":{"$ref":"#/components/schemas/MessageAttachments","nullable":true},"in_reply_to":{"$ref":"#/components/schemas/MessageInReplyTo","nullable":true},"references":{"$ref":"#/components/schemas/MessageReferences","nullable":true},"headers":{"$ref":"#/components/schemas/MessageHeaders","nullable":true},"size":{"$ref":"#/components/schemas/MessageSize"},"updated_at":{"$ref":"#/components/schemas/MessageUpdatedAt"},"created_at":{"$ref":"#/components/schemas/MessageCreatedAt"}},"required":["inbox_id","thread_id","message_id","labels","timestamp","from","to","size","updated_at","created_at"]},"ListMessagesResponse":{"title":"ListMessagesResponse","type":"object","properties":{"count":{"$ref":"#/components/schemas/Count"},"limit":{"$ref":"#/components/schemas/Limit","nullable":true},"next_page_token":{"$ref":"#/components/schemas/PageToken","nullable":true},"messages":{"type":"array","items":{"$ref":"#/components/schemas/MessageItem"},"description":"Ordered by `timestamp` descending."}},"required":["count","messages"]},"SearchMessageHighlights":{"title":"SearchMessageHighlights","type":"object","description":"Matched fragments per field on a message search result, with matched terms\nwrapped in `**`. A field key is present only when the query matched that\nfield, so the present keys also tell you which fields produced the hit.","properties":{"from":{"type":"array","items":{"type":"string"},"nullable":true,"description":"Matched fragments from the sender address."},"recipients":{"type":"array","items":{"type":"string"},"nullable":true,"description":"Matched fragments from the recipient addresses (to, cc, or bcc)."},"subject":{"type":"array","items":{"type":"string"},"nullable":true,"description":"Matched fragments from the subject."},"text":{"type":"array","items":{"type":"string"},"nullable":true,"description":"Matched fragments from the message body."}}},"SearchMessageItem":{"title":"SearchMessageItem","type":"object","properties":{"highlights":{"$ref":"#/components/schemas/SearchMessageHighlights","nullable":true,"description":"Matched fragments per field. Present only when the query matched an indexed field."}},"allOf":[{"$ref":"#/components/schemas/MessageItem"}]},"SearchMessagesResponse":{"title":"SearchMessagesResponse","type":"object","properties":{"count":{"$ref":"#/components/schemas/Count"},"limit":{"$ref":"#/components/schemas/Limit","nullable":true},"next_page_token":{"$ref":"#/components/schemas/PageToken","nullable":true},"messages":{"type":"array","items":{"$ref":"#/components/schemas/SearchMessageItem"},"description":"Ordered by relevance, best match first."}},"required":["count","messages"]},"BatchGetMessagesMessageIds":{"title":"BatchGetMessagesMessageIds","type":"array","items":{"$ref":"#/components/schemas/MessageId"},"description":"IDs of messages to fetch. Maximum 500 ids per request. Duplicates are\nrejected with a validation error. IDs not found in the inbox (including\ncross-inbox or permission-restricted) are silently omitted from the\nresponse; callers detect misses by comparing `count` against `limit`."},"BatchGetMessagesRequest":{"title":"BatchGetMessagesRequest","type":"object","properties":{"message_ids":{"$ref":"#/components/schemas/BatchGetMessagesMessageIds"}},"required":["message_ids"]},"BatchGetMessagesResponse":{"title":"BatchGetMessagesResponse","type":"object","properties":{"limit":{"$ref":"#/components/schemas/Limit"},"count":{"$ref":"#/components/schemas/Count"},"messages":{"type":"array","items":{"$ref":"#/components/schemas/Message"},"description":"Found messages. Order matches `message_ids` in the request. Body\nfields (`text`, `html`, `extracted_text`, `extracted_html`) are\nnever populated; use the single-message endpoint to retrieve bodies."}},"required":["limit","count","messages"]},"BatchUpdateMessagesMessageIds":{"title":"BatchUpdateMessagesMessageIds","type":"array","items":{"$ref":"#/components/schemas/MessageId"},"description":"IDs of messages to update. Maximum 50 ids per request. Duplicates are\nrejected with a validation error. IDs not found in the inbox (including\ncross-inbox or permission-restricted) are silently excluded from the\nupdate; callers detect exclusions by comparing `count` against `limit`."},"BatchUpdateMessagesRequest":{"title":"BatchUpdateMessagesRequest","type":"object","properties":{"message_ids":{"$ref":"#/components/schemas/BatchUpdateMessagesMessageIds"},"add_labels":{"$ref":"#/components/schemas/UpdateMessageLabels","nullable":true,"description":"Label or labels to add to every message."},"remove_labels":{"$ref":"#/components/schemas/UpdateMessageLabels","nullable":true,"description":"Label or labels to remove from every message."}},"required":["message_ids"]},"BatchUpdateMessagesResponse":{"title":"BatchUpdateMessagesResponse","type":"object","properties":{"limit":{"$ref":"#/components/schemas/Limit"},"count":{"$ref":"#/components/schemas/Count"},"updates":{"type":"array","items":{"$ref":"#/components/schemas/UpdateMessageResponse"},"description":"Updated messages with their new labels. Order matches `message_ids`\nin the request. Excluded ids are omitted, so `count` may be less than\n`limit`."}},"required":["limit","count","updates"]},"RawMessageResponse":{"title":"RawMessageResponse","type":"object","description":"S3 presigned URL to download the raw .eml file.","properties":{"message_id":{"$ref":"#/components/schemas/MessageId","description":"ID of the message."},"size":{"$ref":"#/components/schemas/MessageSize","description":"Size of the raw message in bytes."},"download_url":{"type":"string","description":"S3 presigned URL to download the raw message. Expires at expires_at."},"expires_at":{"type":"string","format":"date-time","description":"Time at which the download URL expires."}},"required":["message_id","size","download_url","expires_at"]},"Addresses":{"title":"Addresses","oneOf":[{"type":"string"},{"type":"array","items":{"type":"string"}}]},"SendMessageReplyTo":{"title":"SendMessageReplyTo","$ref":"#/components/schemas/Addresses","description":"Reply-to address or addresses."},"SendMessageTo":{"title":"SendMessageTo","$ref":"#/components/schemas/Addresses","description":"Recipient address or addresses."},"SendMessageCc":{"title":"SendMessageCc","$ref":"#/components/schemas/Addresses","description":"CC recipient address or addresses."},"SendMessageBcc":{"title":"SendMessageBcc","$ref":"#/components/schemas/Addresses","description":"BCC recipient address or addresses."},"SendMessageAttachments":{"title":"SendMessageAttachments","type":"array","items":{"$ref":"#/components/schemas/SendAttachment"},"description":"Attachments to include in message."},"SendMessageHeaders":{"title":"SendMessageHeaders","type":"object","additionalProperties":{"type":"string"},"description":"Headers to include in message."},"TrackOpens":{"title":"TrackOpens","type":"boolean","description":"Track when this message is first opened. Requires a custom domain with tracking enabled and\nan HTML body. Opens surface as the `opened` label on the message and as a `message.opened`\nevent. One pixel is injected per message, not per recipient, so a message with several\nrecipients fires once when any of them opens it, and the event does not identify which one."},"SendMessageRequest":{"title":"SendMessageRequest","type":"object","properties":{"labels":{"$ref":"#/components/schemas/MessageLabels","nullable":true},"reply_to":{"$ref":"#/components/schemas/SendMessageReplyTo","nullable":true},"to":{"$ref":"#/components/schemas/SendMessageTo","nullable":true},"cc":{"$ref":"#/components/schemas/SendMessageCc","nullable":true},"bcc":{"$ref":"#/components/schemas/SendMessageBcc","nullable":true},"subject":{"$ref":"#/components/schemas/MessageSubject","nullable":true},"text":{"$ref":"#/components/schemas/MessageText","nullable":true},"html":{"$ref":"#/components/schemas/MessageHtml","nullable":true},"attachments":{"$ref":"#/components/schemas/SendMessageAttachments","nullable":true},"headers":{"$ref":"#/components/schemas/SendMessageHeaders","nullable":true},"track_opens":{"$ref":"#/components/schemas/TrackOpens","nullable":true}}},"SendMessageResponse":{"title":"SendMessageResponse","type":"object","properties":{"message_id":{"$ref":"#/components/schemas/MessageId"},"thread_id":{"$ref":"#/components/schemas/ThreadId"}},"required":["message_id","thread_id"]},"UpdateMessageResponse":{"title":"UpdateMessageResponse","type":"object","properties":{"message_id":{"$ref":"#/components/schemas/MessageId"},"labels":{"$ref":"#/components/schemas/MessageLabels"}},"required":["message_id","labels"]},"ReplyAll":{"title":"ReplyAll","type":"boolean","description":"Reply to all recipients of the original message."},"ReplyToMessageRequest":{"title":"ReplyToMessageRequest","type":"object","properties":{"labels":{"$ref":"#/components/schemas/MessageLabels","nullable":true},"reply_to":{"$ref":"#/components/schemas/SendMessageReplyTo","nullable":true},"to":{"$ref":"#/components/schemas/SendMessageTo","nullable":true},"cc":{"$ref":"#/components/schemas/SendMessageCc","nullable":true},"bcc":{"$ref":"#/components/schemas/SendMessageBcc","nullable":true},"reply_all":{"$ref":"#/components/schemas/ReplyAll","nullable":true},"text":{"$ref":"#/components/schemas/MessageText","nullable":true},"html":{"$ref":"#/components/schemas/MessageHtml","nullable":true},"attachments":{"$ref":"#/components/schemas/SendMessageAttachments","nullable":true},"headers":{"$ref":"#/components/schemas/SendMessageHeaders","nullable":true},"track_opens":{"$ref":"#/components/schemas/TrackOpens","nullable":true}}},"ReplyAllMessageRequest":{"title":"ReplyAllMessageRequest","type":"object","properties":{"labels":{"$ref":"#/components/schemas/MessageLabels","nullable":true},"reply_to":{"$ref":"#/components/schemas/SendMessageReplyTo","nullable":true},"text":{"$ref":"#/components/schemas/MessageText","nullable":true},"html":{"$ref":"#/components/schemas/MessageHtml","nullable":true},"attachments":{"$ref":"#/components/schemas/SendMessageAttachments","nullable":true},"headers":{"$ref":"#/components/schemas/SendMessageHeaders","nullable":true},"track_opens":{"$ref":"#/components/schemas/TrackOpens","nullable":true}}},"UpdateMessageLabels":{"title":"UpdateMessageLabels","oneOf":[{"type":"string"},{"type":"array","items":{"type":"string"}}],"description":"Label or list of labels."},"UpdateMessageRequest":{"title":"UpdateMessageRequest","type":"object","properties":{"add_labels":{"$ref":"#/components/schemas/UpdateMessageLabels","nullable":true,"description":"Label or labels to add to message."},"remove_labels":{"$ref":"#/components/schemas/UpdateMessageLabels","nullable":true,"description":"Label or labels to remove from message."}}},"MetricEventType":{"title":"MetricEventType","type":"string","enum":["message.received","message.received.spam","message.received.blocked","message.received.unauthenticated","message.sent","message.delivered","message.bounced","message.complained","message.rejected","domain.verified"],"description":"Type of metric event."},"MetricEventTypes":{"title":"MetricEventTypes","type":"array","items":{"$ref":"#/components/schemas/MetricEventType"},"description":"List of metric event types to query."},"Start":{"title":"Start","type":"string","format":"date-time","description":"Start timestamp for the query."},"End":{"title":"End","type":"string","format":"date-time","description":"End timestamp for the query."},"Period":{"title":"Period","type":"integer","description":"Size of each time bucket as a whole number of seconds, between 1 and 86400."},"MetricLimit":{"title":"MetricLimit","type":"integer","description":"Limit on number of buckets to return."},"Descending":{"title":"Descending","type":"boolean","description":"Sort in descending order."},"MetricBucket":{"title":"MetricBucket","type":"object","properties":{"timestamp":{"type":"string","format":"date-time","description":"Timestamp of the bucket."},"count":{"type":"integer","description":"Count of events in the bucket."}},"required":["timestamp","count"]},"QueryMetricsResponse":{"title":"QueryMetricsResponse","type":"object","additionalProperties":{"type":"array","items":{"$ref":"#/components/schemas/MetricBucket"}},"description":"Metrics grouped by event type."},"UsageType":{"title":"UsageType","type":"string","enum":["storage_bytes","message_count","thread_count","inbox_count","pod_count","domain_count"],"description":"Type of usage metric. Inbox-scoped queries carry `storage_bytes`,\n`message_count`, and `thread_count`; pod-scoped queries add `inbox_count`\nand `domain_count`; organization-scoped queries add `pod_count`."},"UsageTypes":{"title":"UsageTypes","type":"array","items":{"$ref":"#/components/schemas/UsageType"},"description":"List of usage metric types to query. Omit to query every type valid for the scope."},"UsagePoint":{"title":"UsagePoint","type":"object","properties":{"timestamp":{"type":"string","format":"date-time","description":"Timestamp of the point."},"value":{"type":"integer","format":"int64","description":"Cumulative value of the usage metric at the timestamp."}},"required":["timestamp","value"]},"QueryUsageResponse":{"title":"QueryUsageResponse","type":"object","additionalProperties":{"type":"array","items":{"$ref":"#/components/schemas/UsagePoint"}},"description":"Cumulative usage series grouped by usage type."},"Organization":{"title":"Organization","type":"object","description":"Organization details with usage limits and counts.","properties":{"organization_id":{"$ref":"#/components/schemas/OrganizationId"},"inbox_count":{"type":"integer","description":"Current number of inboxes."},"domain_count":{"type":"integer","description":"Current number of domains."},"inbox_limit":{"type":"integer","nullable":true,"description":"Maximum number of inboxes allowed."},"domain_limit":{"type":"integer","nullable":true,"description":"Maximum number of domains allowed."},"billing_id":{"type":"string","nullable":true,"description":"Provider-agnostic billing customer ID."},"billing_type":{"type":"string","nullable":true,"description":"Billing provider type (e.g. \"stripe\")."},"billing_subscription_id":{"type":"string","nullable":true,"description":"Active billing subscription ID."},"authentication_id":{"type":"string","nullable":true,"description":"Provider-agnostic authentication ID."},"authentication_type":{"type":"string","nullable":true,"description":"Authentication provider type."},"updated_at":{"type":"string","format":"date-time","description":"Time at which organization was last updated."},"created_at":{"type":"string","format":"date-time","description":"Time at which organization was created."}},"required":["organization_id","inbox_count","domain_count","updated_at","created_at"]},"ThreadId":{"title":"ThreadId","type":"string","description":"ID of thread."},"ThreadLabels":{"title":"ThreadLabels","type":"array","items":{"type":"string"},"description":"Labels of thread."},"ThreadTimestamp":{"title":"ThreadTimestamp","type":"string","format":"date-time","description":"Timestamp of last sent or received message."},"ThreadReceivedTimestamp":{"title":"ThreadReceivedTimestamp","type":"string","format":"date-time","description":"Timestamp of last received message."},"ThreadSentTimestamp":{"title":"ThreadSentTimestamp","type":"string","format":"date-time","description":"Timestamp of last sent message."},"ThreadSenders":{"title":"ThreadSenders","type":"array","items":{"type":"string"},"description":"Senders in thread. In format `username@domain.com` or `Display Name `."},"ThreadRecipients":{"title":"ThreadRecipients","type":"array","items":{"type":"string"},"description":"Recipients in thread. In format `username@domain.com` or `Display Name `."},"ThreadSubject":{"title":"ThreadSubject","type":"string","description":"Subject of thread."},"ThreadPreview":{"title":"ThreadPreview","type":"string","description":"Text preview of last message in thread."},"ThreadAttachments":{"title":"ThreadAttachments","type":"array","items":{"$ref":"#/components/schemas/Attachment"},"description":"Attachments in thread."},"ThreadLastMessageId":{"title":"ThreadLastMessageId","type":"string","description":"ID of last message in thread."},"ThreadMessageCount":{"title":"ThreadMessageCount","type":"integer","description":"Number of messages in thread."},"ThreadSize":{"title":"ThreadSize","type":"integer","description":"Size of thread in bytes."},"ThreadUpdatedAt":{"title":"ThreadUpdatedAt","type":"string","format":"date-time","description":"Time at which thread was last updated."},"ThreadCreatedAt":{"title":"ThreadCreatedAt","type":"string","format":"date-time","description":"Time at which thread was created."},"ThreadItem":{"title":"ThreadItem","type":"object","properties":{"inbox_id":{"$ref":"#/components/schemas/inboxesInboxId"},"thread_id":{"$ref":"#/components/schemas/ThreadId"},"labels":{"$ref":"#/components/schemas/ThreadLabels"},"timestamp":{"$ref":"#/components/schemas/ThreadTimestamp"},"received_timestamp":{"$ref":"#/components/schemas/ThreadReceivedTimestamp","nullable":true},"sent_timestamp":{"$ref":"#/components/schemas/ThreadSentTimestamp","nullable":true},"senders":{"$ref":"#/components/schemas/ThreadSenders"},"recipients":{"$ref":"#/components/schemas/ThreadRecipients"},"subject":{"$ref":"#/components/schemas/ThreadSubject","nullable":true},"preview":{"$ref":"#/components/schemas/ThreadPreview","nullable":true},"attachments":{"$ref":"#/components/schemas/ThreadAttachments","nullable":true},"last_message_id":{"$ref":"#/components/schemas/ThreadLastMessageId"},"message_count":{"$ref":"#/components/schemas/ThreadMessageCount"},"size":{"$ref":"#/components/schemas/ThreadSize"},"updated_at":{"$ref":"#/components/schemas/ThreadUpdatedAt"},"created_at":{"$ref":"#/components/schemas/ThreadCreatedAt"}},"required":["inbox_id","thread_id","labels","timestamp","senders","recipients","last_message_id","message_count","size","updated_at","created_at"]},"Thread":{"title":"Thread","type":"object","properties":{"inbox_id":{"$ref":"#/components/schemas/inboxesInboxId"},"thread_id":{"$ref":"#/components/schemas/ThreadId"},"labels":{"$ref":"#/components/schemas/ThreadLabels"},"timestamp":{"$ref":"#/components/schemas/ThreadTimestamp"},"received_timestamp":{"$ref":"#/components/schemas/ThreadReceivedTimestamp","nullable":true},"sent_timestamp":{"$ref":"#/components/schemas/ThreadSentTimestamp","nullable":true},"senders":{"$ref":"#/components/schemas/ThreadSenders"},"recipients":{"$ref":"#/components/schemas/ThreadRecipients"},"subject":{"$ref":"#/components/schemas/ThreadSubject","nullable":true},"preview":{"$ref":"#/components/schemas/ThreadPreview","nullable":true},"attachments":{"$ref":"#/components/schemas/ThreadAttachments","nullable":true},"last_message_id":{"$ref":"#/components/schemas/ThreadLastMessageId"},"message_count":{"$ref":"#/components/schemas/ThreadMessageCount"},"size":{"$ref":"#/components/schemas/ThreadSize"},"updated_at":{"$ref":"#/components/schemas/ThreadUpdatedAt"},"created_at":{"$ref":"#/components/schemas/ThreadCreatedAt"},"messages":{"type":"array","items":{"$ref":"#/components/schemas/Message"},"description":"Messages in thread. Ordered by `timestamp` ascending."}},"required":["inbox_id","thread_id","labels","timestamp","senders","recipients","last_message_id","message_count","size","updated_at","created_at","messages"]},"UpdateThreadRequest":{"title":"UpdateThreadRequest","type":"object","properties":{"add_labels":{"type":"array","items":{"type":"string"},"nullable":true,"description":"Labels to add to thread. Cannot be system labels."},"remove_labels":{"type":"array","items":{"type":"string"},"nullable":true,"description":"Labels to remove from thread. Cannot be system labels. Takes priority over `add_labels` (in the event of duplicate labels passed in)."}}},"UpdateThreadResponse":{"title":"UpdateThreadResponse","type":"object","properties":{"thread_id":{"$ref":"#/components/schemas/ThreadId"},"labels":{"$ref":"#/components/schemas/ThreadLabels"}},"required":["thread_id","labels"]},"ListThreadsResponse":{"title":"ListThreadsResponse","type":"object","properties":{"count":{"$ref":"#/components/schemas/Count"},"limit":{"$ref":"#/components/schemas/Limit","nullable":true},"next_page_token":{"$ref":"#/components/schemas/PageToken","nullable":true},"threads":{"type":"array","items":{"$ref":"#/components/schemas/ThreadItem"},"description":"Ordered by `timestamp` descending."}},"required":["count","threads"]},"SearchThreadHighlights":{"title":"SearchThreadHighlights","type":"object","description":"Matched fragments per field on a thread search result, with matched terms\nwrapped in `**`. A field key is present only when the query matched that\nfield, so the present keys also tell you which fields produced the hit.","properties":{"from":{"type":"array","items":{"type":"string"},"nullable":true,"description":"Matched fragments from a sender address in the thread."},"recipients":{"type":"array","items":{"type":"string"},"nullable":true,"description":"Matched fragments from a recipient address in the thread (to, cc, or bcc)."},"subject":{"type":"array","items":{"type":"string"},"nullable":true,"description":"Matched fragments from the subject."},"text":{"type":"array","items":{"type":"string"},"nullable":true,"description":"Matched fragments from a message body in the thread."}}},"SearchThreadItem":{"title":"SearchThreadItem","type":"object","properties":{"highlights":{"$ref":"#/components/schemas/SearchThreadHighlights","nullable":true,"description":"Matched fragments per field. Present only when the query matched an indexed field."}},"allOf":[{"$ref":"#/components/schemas/ThreadItem"}]},"SearchThreadsResponse":{"title":"SearchThreadsResponse","type":"object","properties":{"count":{"$ref":"#/components/schemas/Count"},"limit":{"$ref":"#/components/schemas/Limit","nullable":true},"next_page_token":{"$ref":"#/components/schemas/PageToken","nullable":true},"threads":{"type":"array","items":{"$ref":"#/components/schemas/SearchThreadItem"},"description":"Ordered by relevance, best match first."}},"required":["count","threads"]},"webhooksSvixId":{"title":"webhooksSvixId","type":"string","description":"ID of webhook message."},"webhooksSvixTimestamp":{"title":"webhooksSvixTimestamp","type":"string","format":"date-time","description":"Timestamp of webhook message."},"webhooksSvixSignature":{"title":"webhooksSvixSignature","type":"string","description":"Signature of webhook message."},"Subscribe":{"title":"Subscribe","type":"object","properties":{"type":{"type":"string","const":"subscribe"},"event_types":{"$ref":"#/components/schemas/EventTypes","nullable":true},"inbox_ids":{"$ref":"#/components/schemas/InboxIds","nullable":true},"pod_ids":{"$ref":"#/components/schemas/PodIds","nullable":true}},"required":["type"]},"Subscribed":{"title":"Subscribed","type":"object","properties":{"type":{"type":"string","const":"subscribed"},"event_types":{"$ref":"#/components/schemas/EventTypes","nullable":true},"inbox_ids":{"$ref":"#/components/schemas/InboxIds","nullable":true},"pod_ids":{"$ref":"#/components/schemas/PodIds","nullable":true}},"required":["type"]},"Error":{"title":"Error","type":"object","properties":{"type":{"type":"string","const":"error"},"name":{"$ref":"#/components/schemas/ErrorName"},"message":{"$ref":"#/components/schemas/ErrorMessage"}},"required":["type","name","message"]}},"securitySchemes":{"BearerAuth":{"type":"http","scheme":"bearer"},"TokenAuth":{"type":"http","scheme":"bearer"}}}} \ No newline at end of file +{"openapi":"3.0.1","info":{"title":"AgentMail","version":""},"paths":{"/v0/inboxes":{"get":{"description":"**CLI:**\n```bash\nagentmail inboxes list\n```","operationId":"inboxes_list","tags":["Inboxes"],"parameters":[{"name":"limit","in":"query","required":false,"schema":{"$ref":"#/components/schemas/Limit","nullable":true}},{"name":"page_token","in":"query","required":false,"schema":{"$ref":"#/components/schemas/PageToken","nullable":true}},{"name":"ascending","in":"query","required":false,"schema":{"$ref":"#/components/schemas/Ascending","nullable":true}}],"responses":{"200":{"description":"","content":{"application/json":{"schema":{"$ref":"#/components/schemas/inboxesListInboxesResponse"}}}}},"summary":"List Inboxes","servers":[{"url":"https://api.agentmail.to"},{"url":"https://x402.api.agentmail.to"},{"url":"https://mpp.api.agentmail.to"},{"url":"https://api.agentmail.eu"}],"security":[{"BearerAuth":[]},{"TokenAuth":[]}],"x-fern-sdk-group-name":["inboxes"],"x-fern-sdk-method-name":"list"},"post":{"description":"**CLI:**\n```bash\nagentmail inboxes create --display-name \"My Agent\" --username myagent --domain agentmail.to\n```","operationId":"inboxes_create","tags":["Inboxes"],"parameters":[],"responses":{"200":{"description":"","content":{"application/json":{"schema":{"$ref":"#/components/schemas/inboxesInbox"}}}},"400":{"description":"","content":{"application/json":{"schema":{"$ref":"#/components/schemas/ValidationErrorResponse"}}}},"422":{"description":"","content":{"application/json":{"schema":{"$ref":"#/components/schemas/ErrorResponse"}}}}},"summary":"Create Inbox","servers":[{"url":"https://api.agentmail.to"},{"url":"https://x402.api.agentmail.to"},{"url":"https://mpp.api.agentmail.to"},{"url":"https://api.agentmail.eu"}],"security":[{"BearerAuth":[]},{"TokenAuth":[]}],"requestBody":{"required":false,"content":{"application/json":{"schema":{"$ref":"#/components/schemas/inboxesCreateInboxRequest","nullable":true}}}},"x-fern-sdk-group-name":["inboxes"],"x-fern-sdk-method-name":"create"}},"/v0/inboxes/{inbox_id}":{"get":{"description":"**CLI:**\n```bash\nagentmail inboxes get --inbox-id \n```","operationId":"inboxes_get","tags":["Inboxes"],"parameters":[{"name":"inbox_id","in":"path","required":true,"schema":{"$ref":"#/components/schemas/inboxesInboxId"}}],"responses":{"200":{"description":"","content":{"application/json":{"schema":{"$ref":"#/components/schemas/inboxesInbox"}}}},"404":{"description":"","content":{"application/json":{"schema":{"$ref":"#/components/schemas/ErrorResponse"}}}}},"summary":"Get Inbox","servers":[{"url":"https://api.agentmail.to"},{"url":"https://x402.api.agentmail.to"},{"url":"https://mpp.api.agentmail.to"},{"url":"https://api.agentmail.eu"}],"security":[{"BearerAuth":[]},{"TokenAuth":[]}],"x-fern-sdk-group-name":["inboxes"],"x-fern-sdk-method-name":"get"},"patch":{"description":"**CLI:**\n```bash\nagentmail inboxes update --inbox-id --display-name \"Updated Name\"\n```","operationId":"inboxes_update","tags":["Inboxes"],"parameters":[{"name":"inbox_id","in":"path","required":true,"schema":{"$ref":"#/components/schemas/inboxesInboxId"}}],"responses":{"200":{"description":"","content":{"application/json":{"schema":{"$ref":"#/components/schemas/inboxesInbox"}}}},"400":{"description":"","content":{"application/json":{"schema":{"$ref":"#/components/schemas/ValidationErrorResponse"}}}},"404":{"description":"","content":{"application/json":{"schema":{"$ref":"#/components/schemas/ErrorResponse"}}}}},"summary":"Update Inbox","servers":[{"url":"https://api.agentmail.to"},{"url":"https://x402.api.agentmail.to"},{"url":"https://mpp.api.agentmail.to"},{"url":"https://api.agentmail.eu"}],"security":[{"BearerAuth":[]},{"TokenAuth":[]}],"requestBody":{"description":"Expects an object; provide at least one of `display_name` or `metadata`.","required":true,"content":{"application/json":{"schema":{"$ref":"#/components/schemas/inboxesUpdateInboxRequest"}}}},"x-fern-sdk-group-name":["inboxes"],"x-fern-sdk-method-name":"update"},"delete":{"description":"**CLI:**\n```bash\nagentmail inboxes delete --inbox-id \n```","operationId":"inboxes_delete","tags":["Inboxes"],"parameters":[{"name":"inbox_id","in":"path","required":true,"schema":{"$ref":"#/components/schemas/inboxesInboxId"}}],"responses":{"204":{"description":""},"404":{"description":"","content":{"application/json":{"schema":{"$ref":"#/components/schemas/ErrorResponse"}}}}},"summary":"Delete Inbox","servers":[{"url":"https://api.agentmail.to"},{"url":"https://x402.api.agentmail.to"},{"url":"https://mpp.api.agentmail.to"},{"url":"https://api.agentmail.eu"}],"security":[{"BearerAuth":[]},{"TokenAuth":[]}],"x-fern-sdk-group-name":["inboxes"],"x-fern-sdk-method-name":"delete"}},"/v0/pods":{"get":{"description":"**CLI:**\n```bash\nagentmail pods list\n```","operationId":"pods_list","tags":["Pods"],"parameters":[{"name":"limit","in":"query","required":false,"schema":{"$ref":"#/components/schemas/Limit","nullable":true}},{"name":"page_token","in":"query","required":false,"schema":{"$ref":"#/components/schemas/PageToken","nullable":true}},{"name":"ascending","in":"query","required":false,"schema":{"$ref":"#/components/schemas/Ascending","nullable":true}}],"responses":{"200":{"description":"","content":{"application/json":{"schema":{"$ref":"#/components/schemas/podsListPodsResponse"}}}}},"summary":"List Pods","servers":[{"url":"https://api.agentmail.to"},{"url":"https://x402.api.agentmail.to"},{"url":"https://mpp.api.agentmail.to"},{"url":"https://api.agentmail.eu"}],"security":[{"BearerAuth":[]},{"TokenAuth":[]}],"x-fern-sdk-group-name":["pods"],"x-fern-sdk-method-name":"list"},"post":{"description":"**CLI:**\n```bash\nagentmail pods create --client-id my-pod\n```","operationId":"pods_create","tags":["Pods"],"parameters":[],"responses":{"200":{"description":"","content":{"application/json":{"schema":{"$ref":"#/components/schemas/podsPod"}}}},"400":{"description":"","content":{"application/json":{"schema":{"$ref":"#/components/schemas/ValidationErrorResponse"}}}}},"summary":"Create Pod","servers":[{"url":"https://api.agentmail.to"},{"url":"https://x402.api.agentmail.to"},{"url":"https://mpp.api.agentmail.to"},{"url":"https://api.agentmail.eu"}],"security":[{"BearerAuth":[]},{"TokenAuth":[]}],"requestBody":{"required":true,"content":{"application/json":{"schema":{"$ref":"#/components/schemas/podsCreatePodRequest"}}}},"x-fern-sdk-group-name":["pods"],"x-fern-sdk-method-name":"create"}},"/v0/pods/{pod_id}":{"get":{"description":"**CLI:**\n```bash\nagentmail pods get --pod-id \n```","operationId":"pods_get","tags":["Pods"],"parameters":[{"name":"pod_id","in":"path","required":true,"schema":{"$ref":"#/components/schemas/podsPodId"}}],"responses":{"200":{"description":"","content":{"application/json":{"schema":{"$ref":"#/components/schemas/podsPod"}}}},"404":{"description":"","content":{"application/json":{"schema":{"$ref":"#/components/schemas/ErrorResponse"}}}}},"summary":"Get Pod","servers":[{"url":"https://api.agentmail.to"},{"url":"https://x402.api.agentmail.to"},{"url":"https://mpp.api.agentmail.to"},{"url":"https://api.agentmail.eu"}],"security":[{"BearerAuth":[]},{"TokenAuth":[]}],"x-fern-sdk-group-name":["pods"],"x-fern-sdk-method-name":"get"},"delete":{"description":"**CLI:**\n```bash\nagentmail pods delete --pod-id \n```","operationId":"pods_delete","tags":["Pods"],"parameters":[{"name":"pod_id","in":"path","required":true,"schema":{"$ref":"#/components/schemas/podsPodId"}}],"responses":{"204":{"description":""},"404":{"description":"","content":{"application/json":{"schema":{"$ref":"#/components/schemas/ErrorResponse"}}}}},"summary":"Delete Pod","servers":[{"url":"https://api.agentmail.to"},{"url":"https://x402.api.agentmail.to"},{"url":"https://mpp.api.agentmail.to"},{"url":"https://api.agentmail.eu"}],"security":[{"BearerAuth":[]},{"TokenAuth":[]}],"x-fern-sdk-group-name":["pods"],"x-fern-sdk-method-name":"delete"}},"/v0/webhooks":{"get":{"description":"**CLI:**\n```bash\nagentmail webhooks list\n```","operationId":"webhooks_list","tags":["Webhooks"],"parameters":[{"name":"limit","in":"query","required":false,"schema":{"$ref":"#/components/schemas/Limit","nullable":true}},{"name":"page_token","in":"query","required":false,"schema":{"$ref":"#/components/schemas/PageToken","nullable":true}},{"name":"ascending","in":"query","required":false,"schema":{"$ref":"#/components/schemas/Ascending","nullable":true}}],"responses":{"200":{"description":"","content":{"application/json":{"schema":{"$ref":"#/components/schemas/webhooksListWebhooksResponse"}}}}},"summary":"List Webhooks","servers":[{"url":"https://api.agentmail.to"},{"url":"https://x402.api.agentmail.to"},{"url":"https://mpp.api.agentmail.to"},{"url":"https://api.agentmail.eu"}],"security":[{"BearerAuth":[]},{"TokenAuth":[]}],"x-fern-sdk-group-name":["webhooks"],"x-fern-sdk-method-name":"list"},"post":{"description":"**CLI:**\n```bash\nagentmail webhooks create --url https://example.com/webhook --event-types message.received\n```","operationId":"webhooks_create","tags":["Webhooks"],"parameters":[],"responses":{"200":{"description":"","content":{"application/json":{"schema":{"$ref":"#/components/schemas/webhooksWebhook"}}}},"400":{"description":"","content":{"application/json":{"schema":{"$ref":"#/components/schemas/ValidationErrorResponse"}}}}},"summary":"Create Webhook","servers":[{"url":"https://api.agentmail.to"},{"url":"https://x402.api.agentmail.to"},{"url":"https://mpp.api.agentmail.to"},{"url":"https://api.agentmail.eu"}],"security":[{"BearerAuth":[]},{"TokenAuth":[]}],"requestBody":{"required":true,"content":{"application/json":{"schema":{"$ref":"#/components/schemas/webhooksCreateWebhookRequest"}}}},"x-fern-sdk-group-name":["webhooks"],"x-fern-sdk-method-name":"create"}},"/v0/webhooks/{webhook_id}":{"get":{"description":"**CLI:**\n```bash\nagentmail webhooks get --webhook-id \n```","operationId":"webhooks_get","tags":["Webhooks"],"parameters":[{"name":"webhook_id","in":"path","required":true,"schema":{"$ref":"#/components/schemas/webhooksWebhookId"}}],"responses":{"200":{"description":"","content":{"application/json":{"schema":{"$ref":"#/components/schemas/webhooksWebhook"}}}},"404":{"description":"","content":{"application/json":{"schema":{"$ref":"#/components/schemas/ErrorResponse"}}}}},"summary":"Get Webhook","servers":[{"url":"https://api.agentmail.to"},{"url":"https://x402.api.agentmail.to"},{"url":"https://mpp.api.agentmail.to"},{"url":"https://api.agentmail.eu"}],"security":[{"BearerAuth":[]},{"TokenAuth":[]}],"x-fern-sdk-group-name":["webhooks"],"x-fern-sdk-method-name":"get"},"patch":{"description":"Update inbox or pod subscriptions, or replace the webhook's `event_types` in full when you pass a\nnon-empty `event_types` array (see request field docs). Inbox and pod changes use add/remove lists.\n\n**CLI:**\n```bash\nagentmail webhooks update --webhook-id --add-inbox-ids \n```","operationId":"webhooks_update","tags":["Webhooks"],"parameters":[{"name":"webhook_id","in":"path","required":true,"schema":{"$ref":"#/components/schemas/webhooksWebhookId"}}],"responses":{"200":{"description":"","content":{"application/json":{"schema":{"$ref":"#/components/schemas/webhooksWebhook"}}}},"400":{"description":"","content":{"application/json":{"schema":{"$ref":"#/components/schemas/ValidationErrorResponse"}}}},"404":{"description":"","content":{"application/json":{"schema":{"$ref":"#/components/schemas/ErrorResponse"}}}}},"summary":"Update Webhook","servers":[{"url":"https://api.agentmail.to"},{"url":"https://x402.api.agentmail.to"},{"url":"https://mpp.api.agentmail.to"},{"url":"https://api.agentmail.eu"}],"security":[{"BearerAuth":[]},{"TokenAuth":[]}],"requestBody":{"required":true,"content":{"application/json":{"schema":{"$ref":"#/components/schemas/webhooksUpdateWebhookRequest"}}}},"x-fern-sdk-group-name":["webhooks"],"x-fern-sdk-method-name":"update"},"delete":{"description":"**CLI:**\n```bash\nagentmail webhooks delete --webhook-id \n```","operationId":"webhooks_delete","tags":["Webhooks"],"parameters":[{"name":"webhook_id","in":"path","required":true,"schema":{"$ref":"#/components/schemas/webhooksWebhookId"}}],"responses":{"204":{"description":""},"404":{"description":"","content":{"application/json":{"schema":{"$ref":"#/components/schemas/ErrorResponse"}}}}},"summary":"Delete Webhook","servers":[{"url":"https://api.agentmail.to"},{"url":"https://x402.api.agentmail.to"},{"url":"https://mpp.api.agentmail.to"},{"url":"https://api.agentmail.eu"}],"security":[{"BearerAuth":[]},{"TokenAuth":[]}],"x-fern-sdk-group-name":["webhooks"],"x-fern-sdk-method-name":"delete"}},"/v0/webhooks/{webhook_id}/headers":{"get":{"description":"List the names of custom HTTP headers included with deliveries to this webhook. Header values are\nwrite-only and are never returned.","operationId":"webhooks_getHeaders","tags":["Webhooks"],"parameters":[{"name":"webhook_id","in":"path","required":true,"schema":{"$ref":"#/components/schemas/webhooksWebhookId"}}],"responses":{"200":{"description":"","content":{"application/json":{"schema":{"$ref":"#/components/schemas/webhooksWebhookHeaderNamesResponse"}}}},"404":{"description":"","content":{"application/json":{"schema":{"$ref":"#/components/schemas/ErrorResponse"}}}}},"summary":"Get Webhook Headers","servers":[{"url":"https://api.agentmail.to"},{"url":"https://x402.api.agentmail.to"},{"url":"https://mpp.api.agentmail.to"},{"url":"https://api.agentmail.eu"}],"security":[{"BearerAuth":[]},{"TokenAuth":[]}],"x-fern-sdk-group-name":["webhooks"],"x-fern-sdk-method-name":"get-headers"},"patch":{"description":"Atomically set, replace, or remove custom HTTP headers included with deliveries to this webhook.\nHeader values remain write-only.","operationId":"webhooks_updateHeaders","tags":["Webhooks"],"parameters":[{"name":"webhook_id","in":"path","required":true,"schema":{"$ref":"#/components/schemas/webhooksWebhookId"}}],"responses":{"204":{"description":""},"400":{"description":"","content":{"application/json":{"schema":{"$ref":"#/components/schemas/ValidationErrorResponse"}}}},"404":{"description":"","content":{"application/json":{"schema":{"$ref":"#/components/schemas/ErrorResponse"}}}}},"summary":"Update Webhook Headers","servers":[{"url":"https://api.agentmail.to"},{"url":"https://x402.api.agentmail.to"},{"url":"https://mpp.api.agentmail.to"},{"url":"https://api.agentmail.eu"}],"security":[{"BearerAuth":[]},{"TokenAuth":[]}],"requestBody":{"required":true,"content":{"application/json":{"schema":{"$ref":"#/components/schemas/webhooksUpdateWebhookHeadersRequest"}}}},"x-fern-sdk-group-name":["webhooks"],"x-fern-sdk-method-name":"update-headers"}},"/v0/agent/sign-up":{"post":{"description":"Create a new agent organization with an inbox and API key. This endpoint is for signing up for the first time. If you've already signed up, you're all set — just use your existing API key.\n\nA 6-digit OTP is sent to the human's email for verification.\n\nThis endpoint is idempotent. Calling it again with the same `human_email` will rotate the API key and resend the OTP if expired.\n\nThe returned API key has limited permissions until the organization is verified via the verify endpoint.\n\n**CLI:**\n```bash\nagentmail agent sign-up --human-email user@example.com --username my-agent\n```","operationId":"agent_signUp","tags":["Agent"],"parameters":[],"responses":{"200":{"description":"","content":{"application/json":{"schema":{"$ref":"#/components/schemas/AgentSignupResponse"}}}},"400":{"description":"","content":{"application/json":{"schema":{"$ref":"#/components/schemas/ValidationErrorResponse"}}}}},"summary":"Sign Up","servers":[{"url":"https://api.agentmail.to"},{"url":"https://x402.api.agentmail.to"},{"url":"https://mpp.api.agentmail.to"},{"url":"https://api.agentmail.eu"}],"requestBody":{"required":true,"content":{"application/json":{"schema":{"$ref":"#/components/schemas/AgentSignupRequest"}}}},"x-fern-sdk-group-name":["agent"],"x-fern-sdk-method-name":"sign-up"}},"/v0/agent/verify":{"post":{"description":"Verify an agent organization using the 6-digit OTP sent to the human's email during sign-up.\n\nOn success, the organization is upgraded from `agent_unverified` to `agent_verified`, the send allowlist is removed, and free plan entitlements are applied.\n\nThe OTP expires after 24 hours and allows a maximum of 10 attempts. If you run into any difficulties receiving the OTP code, you can also create an account on [console.agentmail.to](https://console.agentmail.to) using the human email address you provided to verify your account.\n\n**CLI:**\n```bash\nagentmail agent verify --otp-code 123456\n```","operationId":"agent_verify","tags":["Agent"],"parameters":[],"responses":{"200":{"description":"","content":{"application/json":{"schema":{"$ref":"#/components/schemas/AgentVerifyResponse"}}}}},"summary":"Verify","servers":[{"url":"https://api.agentmail.to"},{"url":"https://x402.api.agentmail.to"},{"url":"https://mpp.api.agentmail.to"},{"url":"https://api.agentmail.eu"}],"security":[{"BearerAuth":[]},{"TokenAuth":[]}],"requestBody":{"required":true,"content":{"application/json":{"schema":{"$ref":"#/components/schemas/AgentVerifyRequest"}}}},"x-fern-sdk-group-name":["agent"],"x-fern-sdk-method-name":"verify"}},"/v0/api-keys":{"get":{"description":"**CLI:**\n```bash\nagentmail api-keys list\n```","operationId":"apiKeys_list","tags":["ApiKeys"],"parameters":[{"name":"limit","in":"query","required":false,"schema":{"$ref":"#/components/schemas/Limit","nullable":true}},{"name":"page_token","in":"query","required":false,"schema":{"$ref":"#/components/schemas/PageToken","nullable":true}},{"name":"ascending","in":"query","required":false,"schema":{"$ref":"#/components/schemas/Ascending","nullable":true}}],"responses":{"200":{"description":"","content":{"application/json":{"schema":{"$ref":"#/components/schemas/ListApiKeysResponse"}}}}},"summary":"List API Keys","servers":[{"url":"https://api.agentmail.to"},{"url":"https://x402.api.agentmail.to"},{"url":"https://mpp.api.agentmail.to"},{"url":"https://api.agentmail.eu"}],"security":[{"BearerAuth":[]},{"TokenAuth":[]}],"x-fern-sdk-group-name":["apiKeys"],"x-fern-sdk-method-name":"list"},"post":{"description":"**CLI:**\n```bash\nagentmail api-keys create --name \"My Key\"\n```","operationId":"apiKeys_create","tags":["ApiKeys"],"parameters":[],"responses":{"200":{"description":"","content":{"application/json":{"schema":{"$ref":"#/components/schemas/CreateApiKeyResponse"}}}},"400":{"description":"","content":{"application/json":{"schema":{"$ref":"#/components/schemas/ValidationErrorResponse"}}}}},"summary":"Create API Key","servers":[{"url":"https://api.agentmail.to"},{"url":"https://x402.api.agentmail.to"},{"url":"https://mpp.api.agentmail.to"},{"url":"https://api.agentmail.eu"}],"security":[{"BearerAuth":[]},{"TokenAuth":[]}],"requestBody":{"required":true,"content":{"application/json":{"schema":{"$ref":"#/components/schemas/CreateApiKeyRequest"}}}},"x-fern-sdk-group-name":["apiKeys"],"x-fern-sdk-method-name":"create"}},"/v0/api-keys/{api_key_id}":{"delete":{"description":"**CLI:**\n```bash\nagentmail api-keys delete --api-key-id \n```","operationId":"apiKeys_delete","tags":["ApiKeys"],"parameters":[{"name":"api_key_id","in":"path","required":true,"schema":{"$ref":"#/components/schemas/ApiKeyId"}}],"responses":{"204":{"description":""},"404":{"description":"","content":{"application/json":{"schema":{"$ref":"#/components/schemas/ErrorResponse"}}}}},"summary":"Delete API Key","servers":[{"url":"https://api.agentmail.to"},{"url":"https://x402.api.agentmail.to"},{"url":"https://mpp.api.agentmail.to"},{"url":"https://api.agentmail.eu"}],"security":[{"BearerAuth":[]},{"TokenAuth":[]}],"x-fern-sdk-group-name":["apiKeys"],"x-fern-sdk-method-name":"delete"}},"/v0/api-keys/public-keys":{"get":{"description":"List only public-key credentials visible to the bearer caller's scope.\nBearer credentials are never returned, even though both credential types\nshare storage and pagination indexes. Requires `api_key_read`.","operationId":"apiKeys_listPublicKeys","tags":["ApiKeys"],"parameters":[{"name":"limit","in":"query","required":false,"schema":{"$ref":"#/components/schemas/Limit","nullable":true}},{"name":"page_token","in":"query","required":false,"schema":{"$ref":"#/components/schemas/PageToken","nullable":true}},{"name":"ascending","in":"query","required":false,"schema":{"$ref":"#/components/schemas/Ascending","nullable":true}}],"responses":{"200":{"description":"","content":{"application/json":{"schema":{"$ref":"#/components/schemas/ListPublicKeysResponse"}}}}},"summary":"List Public-Key Credentials","servers":[{"url":"https://api.agentmail.to"},{"url":"https://x402.api.agentmail.to"},{"url":"https://mpp.api.agentmail.to"},{"url":"https://api.agentmail.eu"}],"security":[{"BearerAuth":[]},{"TokenAuth":[]}],"x-fern-sdk-group-name":["apiKeys"],"x-fern-sdk-method-name":"list-public-keys"},"post":{"description":"Register a public P-256 JWK using an existing AgentMail bearer API key\nwith `api_key_create`. Re-registering the same JWK creates a new\ncredential ID; it does not replace or recover an earlier credential.\nThe private key must never be sent to AgentMail.","operationId":"apiKeys_createPublicKey","tags":["ApiKeys"],"parameters":[],"responses":{"200":{"description":"","content":{"application/json":{"schema":{"$ref":"#/components/schemas/PublicKeyCredential"}}}},"400":{"description":"","content":{"application/json":{"schema":{"$ref":"#/components/schemas/ValidationErrorResponse"}}}},"409":{"description":"","content":{"application/json":{"schema":{"$ref":"#/components/schemas/ErrorResponse"}}}}},"summary":"Register Public-Key Credential","servers":[{"url":"https://api.agentmail.to"},{"url":"https://x402.api.agentmail.to"},{"url":"https://mpp.api.agentmail.to"},{"url":"https://api.agentmail.eu"}],"security":[{"BearerAuth":[]},{"TokenAuth":[]}],"requestBody":{"required":true,"content":{"application/json":{"schema":{"$ref":"#/components/schemas/CreatePublicKeyRequest"}}}},"x-fern-sdk-group-name":["apiKeys"],"x-fern-sdk-method-name":"create-public-key"}},"/v0/api-keys/public-keys/{api_key_id}":{"patch":{"description":"Rename the credential. All security-relevant fields are immutable.\nRequires `api_key_update`.","operationId":"apiKeys_updatePublicKeyName","tags":["ApiKeys"],"parameters":[{"name":"api_key_id","in":"path","description":"Public-key credential ID returned by registration.","required":true,"schema":{"type":"string","format":"uuid"}}],"responses":{"200":{"description":"","content":{"application/json":{"schema":{"$ref":"#/components/schemas/PublicKeyCredential"}}}},"400":{"description":"","content":{"application/json":{"schema":{"$ref":"#/components/schemas/ValidationErrorResponse"}}}},"404":{"description":"","content":{"application/json":{"schema":{"$ref":"#/components/schemas/ErrorResponse"}}}}},"summary":"Rename Public-Key Credential","servers":[{"url":"https://api.agentmail.to"},{"url":"https://x402.api.agentmail.to"},{"url":"https://mpp.api.agentmail.to"},{"url":"https://api.agentmail.eu"}],"security":[{"BearerAuth":[]},{"TokenAuth":[]}],"requestBody":{"required":true,"content":{"application/json":{"schema":{"$ref":"#/components/schemas/UpdatePublicKeyNameRequest"}}}},"x-fern-sdk-group-name":["apiKeys"],"x-fern-sdk-method-name":"update-public-key-name"},"delete":{"description":"Permanently revoke one public-key credential. This hard-deletes the\ncredential; repeating the request returns not found. Requires\n`api_key_delete`.","operationId":"apiKeys_revokePublicKey","tags":["ApiKeys"],"parameters":[{"name":"api_key_id","in":"path","description":"Public-key credential ID returned by registration.","required":true,"schema":{"type":"string","format":"uuid"}}],"responses":{"204":{"description":""},"404":{"description":"","content":{"application/json":{"schema":{"$ref":"#/components/schemas/ErrorResponse"}}}}},"summary":"Revoke Public-Key Credential","servers":[{"url":"https://api.agentmail.to"},{"url":"https://x402.api.agentmail.to"},{"url":"https://mpp.api.agentmail.to"},{"url":"https://api.agentmail.eu"}],"security":[{"BearerAuth":[]},{"TokenAuth":[]}],"x-fern-sdk-group-name":["apiKeys"],"x-fern-sdk-method-name":"revoke-public-key"}},"/v0/api-keys/public-keys/agentid-sign-in/revoke-all":{"post":{"description":"Invalidate every current public-key credential in the caller's\norganization by advancing its AgentID key generation. The caller must be\norganization-scoped and either have `api_key_delete` or, for a verified\nself-serve agent organization, use an unrestricted unmanaged bearer\ncredential. No request body is accepted.\n\n`Idempotency-Key` is required and must be a UUID. Reusing the same UUID\nreturns the original permanent receipt without advancing the generation\nagain. A new UUID performs a new generation advance.","operationId":"apiKeys_revokeAllAgentIdSignInKeys","tags":["ApiKeys"],"parameters":[{"name":"Idempotency-Key","in":"header","description":"Required UUID identifying this revoke-all operation permanently.","required":true,"schema":{"type":"string","format":"uuid"}}],"responses":{"200":{"description":"","content":{"application/json":{"schema":{"$ref":"#/components/schemas/RevokeAllAgentIdSignInKeysResponse"}}}},"400":{"description":"","content":{"application/json":{"schema":{"$ref":"#/components/schemas/ValidationErrorResponse"}}}},"409":{"description":"","content":{"application/json":{"schema":{"$ref":"#/components/schemas/ErrorResponse"}}}}},"summary":"Revoke All AgentID Sign-In Keys","servers":[{"url":"https://api.agentmail.to"},{"url":"https://x402.api.agentmail.to"},{"url":"https://mpp.api.agentmail.to"},{"url":"https://api.agentmail.eu"}],"security":[{"BearerAuth":[]},{"TokenAuth":[]}],"x-fern-sdk-group-name":["apiKeys"],"x-fern-sdk-method-name":"revoke-all-agent-id-sign-in-keys"}},"/v0/api-keys/browser-credentials":{"get":{"description":"List active browser credentials visible to the caller's scope. Requires `api_key_read`.","operationId":"apiKeys_listBrowserCredentials","tags":["ApiKeys"],"parameters":[{"name":"limit","in":"query","required":false,"schema":{"$ref":"#/components/schemas/BrowserAuthorizationListLimit","nullable":true}},{"name":"page_token","in":"query","required":false,"schema":{"$ref":"#/components/schemas/PageToken","nullable":true}}],"responses":{"200":{"description":"","content":{"application/json":{"schema":{"$ref":"#/components/schemas/ListBrowserCredentialsResponse"}}}}},"summary":"List Browser Credentials","servers":[{"url":"https://api.agentmail.to"},{"url":"https://x402.api.agentmail.to"},{"url":"https://mpp.api.agentmail.to"},{"url":"https://api.agentmail.eu"}],"security":[{"BearerAuth":[]},{"TokenAuth":[]}],"x-fern-sdk-group-name":["apiKeys"],"x-fern-sdk-method-name":"list-browser-credentials"}},"/v0/api-keys/browser-credentials/events":{"get":{"description":"List owner-facing browser credential and consent lifecycle events. Requires `api_key_read`.","operationId":"apiKeys_listBrowserCredentialEvents","tags":["ApiKeys"],"parameters":[{"name":"limit","in":"query","required":false,"schema":{"$ref":"#/components/schemas/BrowserAuthorizationListLimit","nullable":true}},{"name":"page_token","in":"query","required":false,"schema":{"$ref":"#/components/schemas/PageToken","nullable":true}}],"responses":{"200":{"description":"","content":{"application/json":{"schema":{"$ref":"#/components/schemas/ListBrowserLifecycleEventsResponse"}}}},"404":{"description":"","content":{"application/json":{"schema":{"$ref":"#/components/schemas/ErrorResponse"}}}}},"summary":"List Browser Credential Events","servers":[{"url":"https://api.agentmail.to"},{"url":"https://x402.api.agentmail.to"},{"url":"https://mpp.api.agentmail.to"},{"url":"https://api.agentmail.eu"}],"security":[{"BearerAuth":[]},{"TokenAuth":[]}],"x-fern-sdk-group-name":["apiKeys"],"x-fern-sdk-method-name":"list-browser-credential-events"}},"/v0/api-keys/browser-credentials/{credential_id}":{"delete":{"description":"Permanently revoke one active browser credential. Requires `api_key_delete`.","operationId":"apiKeys_deleteBrowserCredential","tags":["ApiKeys"],"parameters":[{"name":"credential_id","in":"path","required":true,"schema":{"type":"string","format":"uuid"}}],"responses":{"204":{"description":""},"404":{"description":"","content":{"application/json":{"schema":{"$ref":"#/components/schemas/ErrorResponse"}}}}},"summary":"Delete Browser Credential","servers":[{"url":"https://api.agentmail.to"},{"url":"https://x402.api.agentmail.to"},{"url":"https://mpp.api.agentmail.to"},{"url":"https://api.agentmail.eu"}],"security":[{"BearerAuth":[]},{"TokenAuth":[]}],"x-fern-sdk-group-name":["apiKeys"],"x-fern-sdk-method-name":"delete-browser-credential"}},"/v0/api-keys/browser-credentials/enrollments/{enrollment_id}":{"delete":{"description":"Cancel one pending, unexpired browser enrollment intent. Requires `api_key_delete`.","operationId":"apiKeys_cancelBrowserEnrollment","tags":["ApiKeys"],"parameters":[{"name":"enrollment_id","in":"path","required":true,"schema":{"type":"string","format":"uuid"}}],"responses":{"204":{"description":""},"404":{"description":"","content":{"application/json":{"schema":{"$ref":"#/components/schemas/ErrorResponse"}}}}},"summary":"Cancel Browser Enrollment","servers":[{"url":"https://api.agentmail.to"},{"url":"https://x402.api.agentmail.to"},{"url":"https://mpp.api.agentmail.to"},{"url":"https://api.agentmail.eu"}],"security":[{"BearerAuth":[]},{"TokenAuth":[]}],"x-fern-sdk-group-name":["apiKeys"],"x-fern-sdk-method-name":"cancel-browser-enrollment"}},"/v0/api-keys/browser-consents":{"get":{"description":"List remembered AgentID client approvals for one live inbox. Requires `api_key_read`.","operationId":"apiKeys_listBrowserConsents","tags":["ApiKeys"],"parameters":[{"name":"inbox_id","in":"query","required":true,"schema":{"type":"string","format":"email","maxLength":254}},{"name":"limit","in":"query","required":false,"schema":{"$ref":"#/components/schemas/BrowserAuthorizationListLimit","nullable":true}},{"name":"page_token","in":"query","required":false,"schema":{"$ref":"#/components/schemas/PageToken","nullable":true}}],"responses":{"200":{"description":"","content":{"application/json":{"schema":{"$ref":"#/components/schemas/ListBrowserConsentsResponse"}}}},"404":{"description":"","content":{"application/json":{"schema":{"$ref":"#/components/schemas/ErrorResponse"}}}}},"summary":"List Browser Consents","servers":[{"url":"https://api.agentmail.to"},{"url":"https://x402.api.agentmail.to"},{"url":"https://mpp.api.agentmail.to"},{"url":"https://api.agentmail.eu"}],"security":[{"BearerAuth":[]},{"TokenAuth":[]}],"x-fern-sdk-group-name":["apiKeys"],"x-fern-sdk-method-name":"list-browser-consents"}},"/v0/api-keys/browser-consents/{consent_id}":{"delete":{"description":"Revoke one remembered AgentID client approval. Requires `api_key_delete`.","operationId":"apiKeys_deleteBrowserConsent","tags":["ApiKeys"],"parameters":[{"name":"consent_id","in":"path","required":true,"schema":{"type":"string"}}],"responses":{"204":{"description":""},"404":{"description":"","content":{"application/json":{"schema":{"$ref":"#/components/schemas/ErrorResponse"}}}}},"summary":"Delete Browser Consent","servers":[{"url":"https://api.agentmail.to"},{"url":"https://x402.api.agentmail.to"},{"url":"https://mpp.api.agentmail.to"},{"url":"https://api.agentmail.eu"}],"security":[{"BearerAuth":[]},{"TokenAuth":[]}],"x-fern-sdk-group-name":["apiKeys"],"x-fern-sdk-method-name":"delete-browser-consent"}},"/v0/auth/me":{"get":{"description":"Returns the identity and scope of the authenticated credential. Useful when a client holds a pod-scoped or inbox-scoped API key and needs to discover the parent organization, pod, or inbox without prior knowledge.\n\n**CLI:**\n```bash\nagentmail auth me\n```","operationId":"auth_me","tags":["Auth"],"parameters":[],"responses":{"200":{"description":"","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Identity"}}}}},"summary":"Who Am I","servers":[{"url":"https://api.agentmail.to"},{"url":"https://x402.api.agentmail.to"},{"url":"https://mpp.api.agentmail.to"},{"url":"https://api.agentmail.eu"}],"security":[{"BearerAuth":[]},{"TokenAuth":[]}],"x-fern-sdk-group-name":["auth"],"x-fern-sdk-method-name":"me"}},"/v0/domains":{"get":{"description":"**CLI:**\n```bash\nagentmail domains list\n```","operationId":"domains_list","tags":["Domains"],"parameters":[{"name":"limit","in":"query","required":false,"schema":{"$ref":"#/components/schemas/Limit","nullable":true}},{"name":"page_token","in":"query","required":false,"schema":{"$ref":"#/components/schemas/PageToken","nullable":true}},{"name":"ascending","in":"query","required":false,"schema":{"$ref":"#/components/schemas/Ascending","nullable":true}}],"responses":{"200":{"description":"","content":{"application/json":{"schema":{"$ref":"#/components/schemas/ListDomainsResponse"}}}}},"summary":"List Domains","servers":[{"url":"https://api.agentmail.to"},{"url":"https://x402.api.agentmail.to"},{"url":"https://mpp.api.agentmail.to"},{"url":"https://api.agentmail.eu"}],"security":[{"BearerAuth":[]},{"TokenAuth":[]}],"x-fern-sdk-group-name":["domains"],"x-fern-sdk-method-name":"list"},"post":{"description":"**CLI:**\n```bash\nagentmail domains create --domain example.com\n```","operationId":"domains_create","tags":["Domains"],"parameters":[],"responses":{"200":{"description":"","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Domain"}}}},"400":{"description":"","content":{"application/json":{"schema":{"$ref":"#/components/schemas/ValidationErrorResponse"}}}}},"summary":"Create Domain","servers":[{"url":"https://api.agentmail.to"},{"url":"https://x402.api.agentmail.to"},{"url":"https://mpp.api.agentmail.to"},{"url":"https://api.agentmail.eu"}],"security":[{"BearerAuth":[]},{"TokenAuth":[]}],"requestBody":{"required":true,"content":{"application/json":{"schema":{"$ref":"#/components/schemas/CreateDomainRequest"}}}},"x-fern-sdk-group-name":["domains"],"x-fern-sdk-method-name":"create"}},"/v0/domains/{domain_id}":{"get":{"description":"**CLI:**\n```bash\nagentmail domains get --domain-id \n```","operationId":"domains_get","tags":["Domains"],"parameters":[{"name":"domain_id","in":"path","required":true,"schema":{"$ref":"#/components/schemas/DomainId"}}],"responses":{"200":{"description":"","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Domain"}}}},"404":{"description":"","content":{"application/json":{"schema":{"$ref":"#/components/schemas/ErrorResponse"}}}}},"summary":"Get Domain","servers":[{"url":"https://api.agentmail.to"},{"url":"https://x402.api.agentmail.to"},{"url":"https://mpp.api.agentmail.to"},{"url":"https://api.agentmail.eu"}],"security":[{"BearerAuth":[]},{"TokenAuth":[]}],"x-fern-sdk-group-name":["domains"],"x-fern-sdk-method-name":"get"},"patch":{"description":"**CLI:**\n```bash\nagentmail domains update --domain-id \n```","operationId":"domains_update","tags":["Domains"],"parameters":[{"name":"domain_id","in":"path","required":true,"schema":{"$ref":"#/components/schemas/DomainId"}}],"responses":{"200":{"description":"","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Domain"}}}},"404":{"description":"","content":{"application/json":{"schema":{"$ref":"#/components/schemas/ErrorResponse"}}}}},"summary":"Update Domain","servers":[{"url":"https://api.agentmail.to"},{"url":"https://x402.api.agentmail.to"},{"url":"https://mpp.api.agentmail.to"},{"url":"https://api.agentmail.eu"}],"security":[{"BearerAuth":[]},{"TokenAuth":[]}],"requestBody":{"required":true,"content":{"application/json":{"schema":{"$ref":"#/components/schemas/UpdateDomainRequest"}}}},"x-fern-sdk-group-name":["domains"],"x-fern-sdk-method-name":"update"},"delete":{"description":"**CLI:**\n```bash\nagentmail domains delete --domain-id \n```","operationId":"domains_delete","tags":["Domains"],"parameters":[{"name":"domain_id","in":"path","required":true,"schema":{"$ref":"#/components/schemas/DomainId"}}],"responses":{"204":{"description":""},"404":{"description":"","content":{"application/json":{"schema":{"$ref":"#/components/schemas/ErrorResponse"}}}}},"summary":"Delete Domain","servers":[{"url":"https://api.agentmail.to"},{"url":"https://x402.api.agentmail.to"},{"url":"https://mpp.api.agentmail.to"},{"url":"https://api.agentmail.eu"}],"security":[{"BearerAuth":[]},{"TokenAuth":[]}],"x-fern-sdk-group-name":["domains"],"x-fern-sdk-method-name":"delete"}},"/v0/domains/{domain_id}/zone-file":{"get":{"description":"**CLI:**\n```bash\nagentmail domains get-zone-file --domain-id \n```","operationId":"domains_getZoneFile","tags":["Domains"],"parameters":[{"name":"domain_id","in":"path","required":true,"schema":{"$ref":"#/components/schemas/DomainId"}}],"responses":{"204":{"description":""},"404":{"description":"","content":{"application/json":{"schema":{"$ref":"#/components/schemas/ErrorResponse"}}}}},"summary":"Get Zone File","servers":[{"url":"https://api.agentmail.to"},{"url":"https://x402.api.agentmail.to"},{"url":"https://mpp.api.agentmail.to"},{"url":"https://api.agentmail.eu"}],"security":[{"BearerAuth":[]},{"TokenAuth":[]}],"x-fern-sdk-group-name":["domains"],"x-fern-sdk-method-name":"get-zone-file"}},"/v0/domains/{domain_id}/verify":{"post":{"description":"**CLI:**\n```bash\nagentmail domains verify --domain-id \n```","operationId":"domains_verify","tags":["Domains"],"parameters":[{"name":"domain_id","in":"path","required":true,"schema":{"$ref":"#/components/schemas/DomainId"}}],"responses":{"204":{"description":""},"404":{"description":"","content":{"application/json":{"schema":{"$ref":"#/components/schemas/ErrorResponse"}}}}},"summary":"Verify Domain","servers":[{"url":"https://api.agentmail.to"},{"url":"https://x402.api.agentmail.to"},{"url":"https://mpp.api.agentmail.to"},{"url":"https://api.agentmail.eu"}],"security":[{"BearerAuth":[]},{"TokenAuth":[]}],"x-fern-sdk-group-name":["domains"],"x-fern-sdk-method-name":"verify"}},"/v0/drafts":{"get":{"description":"**CLI:**\n```bash\nagentmail drafts list\n```","operationId":"drafts_list","tags":["Drafts"],"parameters":[{"name":"limit","in":"query","required":false,"schema":{"$ref":"#/components/schemas/Limit","nullable":true}},{"name":"page_token","in":"query","required":false,"schema":{"$ref":"#/components/schemas/PageToken","nullable":true}},{"name":"labels","in":"query","required":false,"schema":{"$ref":"#/components/schemas/Labels","nullable":true}},{"name":"before","in":"query","required":false,"schema":{"$ref":"#/components/schemas/Before","nullable":true}},{"name":"after","in":"query","required":false,"schema":{"$ref":"#/components/schemas/After","nullable":true}},{"name":"ascending","in":"query","required":false,"schema":{"$ref":"#/components/schemas/Ascending","nullable":true}}],"responses":{"200":{"description":"","content":{"application/json":{"schema":{"$ref":"#/components/schemas/ListDraftsResponse"}}}},"404":{"description":"","content":{"application/json":{"schema":{"$ref":"#/components/schemas/ErrorResponse"}}}}},"summary":"List Drafts","servers":[{"url":"https://api.agentmail.to"},{"url":"https://x402.api.agentmail.to"},{"url":"https://mpp.api.agentmail.to"},{"url":"https://api.agentmail.eu"}],"security":[{"BearerAuth":[]},{"TokenAuth":[]}],"x-fern-sdk-group-name":["drafts"],"x-fern-sdk-method-name":"list"}},"/v0/drafts/{draft_id}":{"get":{"description":"**CLI:**\n```bash\nagentmail drafts get --draft-id \n```","operationId":"drafts_get","tags":["Drafts"],"parameters":[{"name":"draft_id","in":"path","required":true,"schema":{"$ref":"#/components/schemas/DraftId"}}],"responses":{"200":{"description":"","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Draft"}}}},"404":{"description":"","content":{"application/json":{"schema":{"$ref":"#/components/schemas/ErrorResponse"}}}}},"summary":"Get Draft","servers":[{"url":"https://api.agentmail.to"},{"url":"https://x402.api.agentmail.to"},{"url":"https://mpp.api.agentmail.to"},{"url":"https://api.agentmail.eu"}],"security":[{"BearerAuth":[]},{"TokenAuth":[]}],"x-fern-sdk-group-name":["drafts"],"x-fern-sdk-method-name":"get"}},"/v0/drafts/{draft_id}/attachments/{attachment_id}":{"get":{"description":"**CLI:**\n```bash\nagentmail drafts get-attachment --draft-id --attachment-id \n```","operationId":"drafts_getAttachment","tags":["Drafts"],"parameters":[{"name":"draft_id","in":"path","required":true,"schema":{"$ref":"#/components/schemas/DraftId"}},{"name":"attachment_id","in":"path","required":true,"schema":{"$ref":"#/components/schemas/AttachmentId"}}],"responses":{"200":{"description":"","content":{"application/json":{"schema":{"$ref":"#/components/schemas/AttachmentResponse"}}}},"404":{"description":"","content":{"application/json":{"schema":{"$ref":"#/components/schemas/ErrorResponse"}}}}},"summary":"Get Attachment","servers":[{"url":"https://api.agentmail.to"},{"url":"https://x402.api.agentmail.to"},{"url":"https://mpp.api.agentmail.to"},{"url":"https://api.agentmail.eu"}],"security":[{"BearerAuth":[]},{"TokenAuth":[]}],"x-fern-sdk-group-name":["drafts"],"x-fern-sdk-method-name":"get-attachment"}},"/v0/inboxes/{inbox_id}/api-keys":{"get":{"description":"**CLI:**\n```bash\nagentmail inboxes api-keys list --inbox-id \n```","operationId":"inboxes_apiKeys_list","tags":["InboxesApiKeys"],"parameters":[{"name":"inbox_id","in":"path","required":true,"schema":{"$ref":"#/components/schemas/inboxesInboxId"}},{"name":"limit","in":"query","required":false,"schema":{"$ref":"#/components/schemas/Limit","nullable":true}},{"name":"page_token","in":"query","required":false,"schema":{"$ref":"#/components/schemas/PageToken","nullable":true}}],"responses":{"200":{"description":"","content":{"application/json":{"schema":{"$ref":"#/components/schemas/ListApiKeysResponse"}}}},"404":{"description":"","content":{"application/json":{"schema":{"$ref":"#/components/schemas/ErrorResponse"}}}}},"summary":"List API Keys","servers":[{"url":"https://api.agentmail.to"},{"url":"https://x402.api.agentmail.to"},{"url":"https://mpp.api.agentmail.to"},{"url":"https://api.agentmail.eu"}],"security":[{"BearerAuth":[]},{"TokenAuth":[]}],"x-fern-sdk-group-name":["inboxes","apiKeys"],"x-fern-sdk-method-name":"list"},"post":{"description":"**CLI:**\n```bash\nagentmail inboxes api-keys create --inbox-id --name \"My Key\"\n```","operationId":"inboxes_apiKeys_create","tags":["InboxesApiKeys"],"parameters":[{"name":"inbox_id","in":"path","required":true,"schema":{"$ref":"#/components/schemas/inboxesInboxId"}}],"responses":{"200":{"description":"","content":{"application/json":{"schema":{"$ref":"#/components/schemas/CreateApiKeyResponse"}}}},"400":{"description":"","content":{"application/json":{"schema":{"$ref":"#/components/schemas/ValidationErrorResponse"}}}},"404":{"description":"","content":{"application/json":{"schema":{"$ref":"#/components/schemas/ErrorResponse"}}}}},"summary":"Create API Key","servers":[{"url":"https://api.agentmail.to"},{"url":"https://x402.api.agentmail.to"},{"url":"https://mpp.api.agentmail.to"},{"url":"https://api.agentmail.eu"}],"security":[{"BearerAuth":[]},{"TokenAuth":[]}],"requestBody":{"required":true,"content":{"application/json":{"schema":{"$ref":"#/components/schemas/CreateApiKeyRequest"}}}},"x-fern-sdk-group-name":["inboxes","apiKeys"],"x-fern-sdk-method-name":"create"}},"/v0/inboxes/{inbox_id}/api-keys/{api_key_id}":{"delete":{"description":"**CLI:**\n```bash\nagentmail inboxes api-keys delete --inbox-id --api-key-id \n```","operationId":"inboxes_apiKeys_delete","tags":["InboxesApiKeys"],"parameters":[{"name":"inbox_id","in":"path","required":true,"schema":{"$ref":"#/components/schemas/inboxesInboxId"}},{"name":"api_key_id","in":"path","required":true,"schema":{"$ref":"#/components/schemas/ApiKeyId"}}],"responses":{"204":{"description":""},"404":{"description":"","content":{"application/json":{"schema":{"$ref":"#/components/schemas/ErrorResponse"}}}}},"summary":"Delete API Key","servers":[{"url":"https://api.agentmail.to"},{"url":"https://x402.api.agentmail.to"},{"url":"https://mpp.api.agentmail.to"},{"url":"https://api.agentmail.eu"}],"security":[{"BearerAuth":[]},{"TokenAuth":[]}],"x-fern-sdk-group-name":["inboxes","apiKeys"],"x-fern-sdk-method-name":"delete"}},"/v0/inboxes/{inbox_id}/browser-credentials/enrollments":{"post":{"description":"Attach a browser enrollment intent to the inbox. Requires\n`api_key_create`. Before submitting `transaction_jti`, independently\nverify that the browser page's final origin is exactly\n`https://auth.agentid.com`.\n\nThis endpoint is available to every organization using US production.\nIt is not available in EU production.\n\nSelect `inbox_id` from trusted AgentMail configuration. An AgentID\n`login_hint` is not authoritative for selecting the inbox; when the\ntransaction includes one, it must match the path inbox.\n\n**AgentMail API keys are sent only to `https://api.agentmail.to`; AgentID never requests them.**\n\nA new intent returns `202`; an idempotent retry for the same pending\ntransaction, inbox, and bearer key returns `200` with the same receipt.\nAn intent lasts at most five minutes. An activated credential lasts at\nmost 30 days and cannot outlive its authorizing bearer API key.\n\nCreation is limited to 20 intents per bearer API key per hour, 100 per\norganization per hour, and five live unused intents per bearer API key.\nBrowser activation is separately limited to 20 activations per\nauthorizing bearer API key per UTC day. Either kind of limit can return\n`429`; honor the `Retry-After` header. Cancelling an enrollment releases\nits live-intent slot but does not reset the daily activation counter.","operationId":"inboxes_browserCredentials_createEnrollment","tags":["InboxesBrowserCredentials"],"parameters":[{"name":"inbox_id","in":"path","required":true,"schema":{"$ref":"#/components/schemas/inboxesInboxId"}}],"responses":{"202":{"description":"","content":{"application/json":{"schema":{"$ref":"#/components/schemas/BrowserEnrollmentAccepted"}}}},"400":{"description":"","content":{"application/json":{"schema":{"$ref":"#/components/schemas/ValidationErrorResponse"}}}},"404":{"description":"","content":{"application/json":{"schema":{"$ref":"#/components/schemas/ErrorResponse"}}}},"409":{"description":"","content":{"application/json":{"schema":{"$ref":"#/components/schemas/ErrorResponse"}}}}},"summary":"Create Browser Enrollment","servers":[{"url":"https://api.agentmail.to"},{"url":"https://x402.api.agentmail.to"},{"url":"https://mpp.api.agentmail.to"},{"url":"https://api.agentmail.eu"}],"security":[{"BearerAuth":[]},{"TokenAuth":[]}],"requestBody":{"required":true,"content":{"application/json":{"schema":{"$ref":"#/components/schemas/CreateBrowserEnrollmentRequest"}}}},"x-fern-sdk-group-name":["inboxes","browserCredentials"],"x-fern-sdk-method-name":"create-enrollment"}},"/v0/inboxes/{inbox_id}/drafts":{"get":{"description":"**CLI:**\n```bash\nagentmail inboxes drafts list --inbox-id \n```","operationId":"inboxes_drafts_list","tags":["InboxesDrafts"],"parameters":[{"name":"inbox_id","in":"path","required":true,"schema":{"$ref":"#/components/schemas/inboxesInboxId"}},{"name":"limit","in":"query","required":false,"schema":{"$ref":"#/components/schemas/Limit","nullable":true}},{"name":"page_token","in":"query","required":false,"schema":{"$ref":"#/components/schemas/PageToken","nullable":true}},{"name":"labels","in":"query","required":false,"schema":{"$ref":"#/components/schemas/Labels","nullable":true}},{"name":"before","in":"query","required":false,"schema":{"$ref":"#/components/schemas/Before","nullable":true}},{"name":"after","in":"query","required":false,"schema":{"$ref":"#/components/schemas/After","nullable":true}},{"name":"ascending","in":"query","required":false,"schema":{"$ref":"#/components/schemas/Ascending","nullable":true}}],"responses":{"200":{"description":"","content":{"application/json":{"schema":{"$ref":"#/components/schemas/ListDraftsResponse"}}}},"404":{"description":"","content":{"application/json":{"schema":{"$ref":"#/components/schemas/ErrorResponse"}}}}},"summary":"List Drafts","servers":[{"url":"https://api.agentmail.to"},{"url":"https://x402.api.agentmail.to"},{"url":"https://mpp.api.agentmail.to"},{"url":"https://api.agentmail.eu"}],"security":[{"BearerAuth":[]},{"TokenAuth":[]}],"x-fern-sdk-group-name":["inboxes","drafts"],"x-fern-sdk-method-name":"list"},"post":{"description":"Create a draft. Supply `in_reply_to` to create a reply draft (with\n`reply_all` to address the whole thread), whose recipients, subject, and\nthreading are derived from the referenced message, or `forward_of` to\ncreate a forward draft, which derives the subject, threading, and\nforwarded content from the source but keeps recipients caller-supplied.\n\n**CLI:**\n```bash\nagentmail inboxes drafts create --inbox-id --to recipient@example.com --subject \"Draft subject\" --text \"Draft body\"\n```","operationId":"inboxes_drafts_create","tags":["InboxesDrafts"],"parameters":[{"name":"inbox_id","in":"path","required":true,"schema":{"$ref":"#/components/schemas/inboxesInboxId"}}],"responses":{"200":{"description":"","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Draft"}}}},"400":{"description":"","content":{"application/json":{"schema":{"$ref":"#/components/schemas/ValidationErrorResponse"}}}},"404":{"description":"","content":{"application/json":{"schema":{"$ref":"#/components/schemas/ErrorResponse"}}}}},"summary":"Create Draft","servers":[{"url":"https://api.agentmail.to"},{"url":"https://x402.api.agentmail.to"},{"url":"https://mpp.api.agentmail.to"},{"url":"https://api.agentmail.eu"}],"security":[{"BearerAuth":[]},{"TokenAuth":[]}],"requestBody":{"required":true,"content":{"application/json":{"schema":{"$ref":"#/components/schemas/CreateDraftRequest"}}}},"x-fern-sdk-group-name":["inboxes","drafts"],"x-fern-sdk-method-name":"create"}},"/v0/inboxes/{inbox_id}/drafts/{draft_id}":{"get":{"description":"**CLI:**\n```bash\nagentmail inboxes drafts get --inbox-id --draft-id \n```","operationId":"inboxes_drafts_get","tags":["InboxesDrafts"],"parameters":[{"name":"inbox_id","in":"path","required":true,"schema":{"$ref":"#/components/schemas/inboxesInboxId"}},{"name":"draft_id","in":"path","required":true,"schema":{"$ref":"#/components/schemas/DraftId"}}],"responses":{"200":{"description":"","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Draft"}}}},"404":{"description":"","content":{"application/json":{"schema":{"$ref":"#/components/schemas/ErrorResponse"}}}}},"summary":"Get Draft","servers":[{"url":"https://api.agentmail.to"},{"url":"https://x402.api.agentmail.to"},{"url":"https://mpp.api.agentmail.to"},{"url":"https://api.agentmail.eu"}],"security":[{"BearerAuth":[]},{"TokenAuth":[]}],"x-fern-sdk-group-name":["inboxes","drafts"],"x-fern-sdk-method-name":"get"},"patch":{"description":"Edit fields on an existing draft. Passing `null` clears a field (or `[]`\nfor a recipient field); `send_at: null` un-schedules a scheduled draft.\nA draft that is already being sent cannot be edited.\n\n**CLI:**\n```bash\nagentmail inboxes drafts update --inbox-id --draft-id --subject \"Updated subject\"\n```","operationId":"inboxes_drafts_update","tags":["InboxesDrafts"],"parameters":[{"name":"inbox_id","in":"path","required":true,"schema":{"$ref":"#/components/schemas/inboxesInboxId"}},{"name":"draft_id","in":"path","required":true,"schema":{"$ref":"#/components/schemas/DraftId"}}],"responses":{"200":{"description":"","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Draft"}}}},"400":{"description":"","content":{"application/json":{"schema":{"$ref":"#/components/schemas/ValidationErrorResponse"}}}},"404":{"description":"","content":{"application/json":{"schema":{"$ref":"#/components/schemas/ErrorResponse"}}}},"409":{"description":"","content":{"application/json":{"schema":{"$ref":"#/components/schemas/ErrorResponse"}}}}},"summary":"Update Draft","servers":[{"url":"https://api.agentmail.to"},{"url":"https://x402.api.agentmail.to"},{"url":"https://mpp.api.agentmail.to"},{"url":"https://api.agentmail.eu"}],"security":[{"BearerAuth":[]},{"TokenAuth":[]}],"requestBody":{"required":true,"content":{"application/json":{"schema":{"$ref":"#/components/schemas/UpdateDraftRequest"}}}},"x-fern-sdk-group-name":["inboxes","drafts"],"x-fern-sdk-method-name":"update"},"delete":{"description":"**CLI:**\n```bash\nagentmail inboxes drafts delete --inbox-id --draft-id \n```","operationId":"inboxes_drafts_delete","tags":["InboxesDrafts"],"parameters":[{"name":"inbox_id","in":"path","required":true,"schema":{"$ref":"#/components/schemas/inboxesInboxId"}},{"name":"draft_id","in":"path","required":true,"schema":{"$ref":"#/components/schemas/DraftId"}}],"responses":{"204":{"description":""},"404":{"description":"","content":{"application/json":{"schema":{"$ref":"#/components/schemas/ErrorResponse"}}}}},"summary":"Delete Draft","servers":[{"url":"https://api.agentmail.to"},{"url":"https://x402.api.agentmail.to"},{"url":"https://mpp.api.agentmail.to"},{"url":"https://api.agentmail.eu"}],"security":[{"BearerAuth":[]},{"TokenAuth":[]}],"x-fern-sdk-group-name":["inboxes","drafts"],"x-fern-sdk-method-name":"delete"}},"/v0/inboxes/{inbox_id}/drafts/{draft_id}/attachments/{attachment_id}":{"get":{"description":"**CLI:**\n```bash\nagentmail inboxes drafts get-attachment --inbox-id --draft-id --attachment-id \n```","operationId":"inboxes_drafts_getAttachment","tags":["InboxesDrafts"],"parameters":[{"name":"inbox_id","in":"path","required":true,"schema":{"$ref":"#/components/schemas/inboxesInboxId"}},{"name":"draft_id","in":"path","required":true,"schema":{"$ref":"#/components/schemas/DraftId"}},{"name":"attachment_id","in":"path","required":true,"schema":{"$ref":"#/components/schemas/AttachmentId"}}],"responses":{"200":{"description":"","content":{"application/json":{"schema":{"$ref":"#/components/schemas/AttachmentResponse"}}}},"404":{"description":"","content":{"application/json":{"schema":{"$ref":"#/components/schemas/ErrorResponse"}}}}},"summary":"Get Attachment","servers":[{"url":"https://api.agentmail.to"},{"url":"https://x402.api.agentmail.to"},{"url":"https://mpp.api.agentmail.to"},{"url":"https://api.agentmail.eu"}],"security":[{"BearerAuth":[]},{"TokenAuth":[]}],"x-fern-sdk-group-name":["inboxes","drafts"],"x-fern-sdk-method-name":"get-attachment"}},"/v0/inboxes/{inbox_id}/drafts/{draft_id}/send":{"post":{"description":"**CLI:**\n```bash\nagentmail inboxes drafts send --inbox-id --draft-id \n```","operationId":"inboxes_drafts_send","tags":["InboxesDrafts"],"parameters":[{"name":"inbox_id","in":"path","required":true,"schema":{"$ref":"#/components/schemas/inboxesInboxId"}},{"name":"draft_id","in":"path","required":true,"schema":{"$ref":"#/components/schemas/DraftId"}},{"name":"Idempotency-Key","in":"header","required":false,"description":"Unique key that makes a send idempotent. A retry carrying the same key returns the original message instead of sending a second email; reusing a key with a different request returns a 409 conflict. Keys expire 24 hours after the send completes.","schema":{"type":"string"}}],"responses":{"200":{"description":"","content":{"application/json":{"schema":{"$ref":"#/components/schemas/SendMessageResponse"}}}},"400":{"description":"","content":{"application/json":{"schema":{"$ref":"#/components/schemas/ValidationErrorResponse"}}}},"403":{"description":"","content":{"application/json":{"schema":{"$ref":"#/components/schemas/ErrorResponse"}}}},"404":{"description":"","content":{"application/json":{"schema":{"$ref":"#/components/schemas/ErrorResponse"}}}},"409":{"description":"","content":{"application/json":{"schema":{"$ref":"#/components/schemas/ErrorResponse"}}}}},"summary":"Send Draft","servers":[{"url":"https://api.agentmail.to"},{"url":"https://x402.api.agentmail.to"},{"url":"https://mpp.api.agentmail.to"},{"url":"https://api.agentmail.eu"}],"security":[{"BearerAuth":[]},{"TokenAuth":[]}],"requestBody":{"required":true,"content":{"application/json":{"schema":{"$ref":"#/components/schemas/UpdateMessageRequest"}}}},"x-fern-idempotent":true,"x-fern-sdk-group-name":["inboxes","drafts"],"x-fern-sdk-method-name":"send"}},"/v0/inboxes/{inbox_id}/events":{"get":{"description":"List label change events for an inbox. Returns events in reverse chronological order by default. Use for IMAP UID projection or audit logging.\n\n**CLI:**\n```bash\nagentmail inboxes events list --inbox-id \n```","operationId":"inboxes_events_list","tags":["InboxesEvents"],"parameters":[{"name":"inbox_id","in":"path","required":true,"schema":{"$ref":"#/components/schemas/inboxesInboxId"}},{"name":"limit","in":"query","required":false,"schema":{"$ref":"#/components/schemas/Limit","nullable":true}},{"name":"page_token","in":"query","required":false,"schema":{"$ref":"#/components/schemas/PageToken","nullable":true}},{"name":"ascending","in":"query","required":false,"schema":{"$ref":"#/components/schemas/Ascending","nullable":true}}],"responses":{"200":{"description":"","content":{"application/json":{"schema":{"$ref":"#/components/schemas/ListInboxEventsResponse"}}}},"404":{"description":"","content":{"application/json":{"schema":{"$ref":"#/components/schemas/ErrorResponse"}}}}},"summary":"List Inbox Events","servers":[{"url":"https://api.agentmail.to"},{"url":"https://x402.api.agentmail.to"},{"url":"https://mpp.api.agentmail.to"},{"url":"https://api.agentmail.eu"}],"security":[{"BearerAuth":[]},{"TokenAuth":[]}],"x-fern-sdk-group-name":["inboxes","events"],"x-fern-sdk-method-name":"list"}},"/v0/inboxes/{inbox_id}/lists/{direction}/{type}":{"get":{"description":"**CLI:**\n```bash\nagentmail inboxes lists list --inbox-id --direction --type \n```","operationId":"inboxes_lists_list","tags":["InboxesLists"],"parameters":[{"name":"inbox_id","in":"path","required":true,"schema":{"$ref":"#/components/schemas/inboxesInboxId"}},{"name":"direction","in":"path","required":true,"schema":{"$ref":"#/components/schemas/Direction"}},{"name":"type","in":"path","required":true,"schema":{"$ref":"#/components/schemas/ListType"}},{"name":"limit","in":"query","required":false,"schema":{"$ref":"#/components/schemas/Limit","nullable":true}},{"name":"page_token","in":"query","required":false,"schema":{"$ref":"#/components/schemas/PageToken","nullable":true}}],"responses":{"200":{"description":"","content":{"application/json":{"schema":{"$ref":"#/components/schemas/PodListListEntriesResponse"}}}}},"summary":"List Entries","servers":[{"url":"https://api.agentmail.to"},{"url":"https://x402.api.agentmail.to"},{"url":"https://mpp.api.agentmail.to"},{"url":"https://api.agentmail.eu"}],"security":[{"BearerAuth":[]},{"TokenAuth":[]}],"x-fern-sdk-group-name":["inboxes","lists"],"x-fern-sdk-method-name":"list"},"post":{"description":"**CLI:**\n```bash\nagentmail inboxes lists create --inbox-id --direction --type --entry user@example.com\n```","operationId":"inboxes_lists_create","tags":["InboxesLists"],"parameters":[{"name":"inbox_id","in":"path","required":true,"schema":{"$ref":"#/components/schemas/inboxesInboxId"}},{"name":"direction","in":"path","required":true,"schema":{"$ref":"#/components/schemas/Direction"}},{"name":"type","in":"path","required":true,"schema":{"$ref":"#/components/schemas/ListType"}}],"responses":{"200":{"description":"","content":{"application/json":{"schema":{"$ref":"#/components/schemas/PodListEntry"}}}},"400":{"description":"","content":{"application/json":{"schema":{"$ref":"#/components/schemas/ValidationErrorResponse"}}}}},"summary":"Create List Entry","servers":[{"url":"https://api.agentmail.to"},{"url":"https://x402.api.agentmail.to"},{"url":"https://mpp.api.agentmail.to"},{"url":"https://api.agentmail.eu"}],"security":[{"BearerAuth":[]},{"TokenAuth":[]}],"requestBody":{"required":true,"content":{"application/json":{"schema":{"$ref":"#/components/schemas/CreateListEntryRequest"}}}},"x-fern-sdk-group-name":["inboxes","lists"],"x-fern-sdk-method-name":"create"}},"/v0/inboxes/{inbox_id}/lists/{direction}/{type}/{entry}":{"get":{"description":"**CLI:**\n```bash\nagentmail inboxes lists get --inbox-id --direction --type --entry \n```","operationId":"inboxes_lists_get","tags":["InboxesLists"],"parameters":[{"name":"inbox_id","in":"path","required":true,"schema":{"$ref":"#/components/schemas/inboxesInboxId"}},{"name":"direction","in":"path","required":true,"schema":{"$ref":"#/components/schemas/Direction"}},{"name":"type","in":"path","required":true,"schema":{"$ref":"#/components/schemas/ListType"}},{"name":"entry","in":"path","description":"Email address or domain.","required":true,"schema":{"type":"string"}}],"responses":{"200":{"description":"","content":{"application/json":{"schema":{"$ref":"#/components/schemas/PodListEntry"}}}},"404":{"description":"","content":{"application/json":{"schema":{"$ref":"#/components/schemas/ErrorResponse"}}}}},"summary":"Get List Entry","servers":[{"url":"https://api.agentmail.to"},{"url":"https://x402.api.agentmail.to"},{"url":"https://mpp.api.agentmail.to"},{"url":"https://api.agentmail.eu"}],"security":[{"BearerAuth":[]},{"TokenAuth":[]}],"x-fern-sdk-group-name":["inboxes","lists"],"x-fern-sdk-method-name":"get"},"delete":{"description":"**CLI:**\n```bash\nagentmail inboxes lists delete --inbox-id --direction --type --entry \n```","operationId":"inboxes_lists_delete","tags":["InboxesLists"],"parameters":[{"name":"inbox_id","in":"path","required":true,"schema":{"$ref":"#/components/schemas/inboxesInboxId"}},{"name":"direction","in":"path","required":true,"schema":{"$ref":"#/components/schemas/Direction"}},{"name":"type","in":"path","required":true,"schema":{"$ref":"#/components/schemas/ListType"}},{"name":"entry","in":"path","description":"Email address or domain.","required":true,"schema":{"type":"string"}}],"responses":{"204":{"description":""},"404":{"description":"","content":{"application/json":{"schema":{"$ref":"#/components/schemas/ErrorResponse"}}}}},"summary":"Delete List Entry","servers":[{"url":"https://api.agentmail.to"},{"url":"https://x402.api.agentmail.to"},{"url":"https://mpp.api.agentmail.to"},{"url":"https://api.agentmail.eu"}],"security":[{"BearerAuth":[]},{"TokenAuth":[]}],"x-fern-sdk-group-name":["inboxes","lists"],"x-fern-sdk-method-name":"delete"}},"/v0/inboxes/{inbox_id}/messages":{"get":{"description":"Lists messages in the inbox, most recent first. Pass `from`, `to`, or\n`subject` to filter by substring. Filtered requests are served by\nsearch, which caps `limit` at 100. For relevance-ranked full-text\nsearch across sender, recipients, subject, and message body, use\n`Search Messages`.\n\n**CLI:**\n```bash\nagentmail inboxes messages list --inbox-id \n```","operationId":"inboxes_messages_list","tags":["InboxesMessages"],"parameters":[{"name":"inbox_id","in":"path","required":true,"schema":{"$ref":"#/components/schemas/inboxesInboxId"}},{"name":"limit","in":"query","required":false,"schema":{"$ref":"#/components/schemas/Limit","nullable":true}},{"name":"page_token","in":"query","required":false,"schema":{"$ref":"#/components/schemas/PageToken","nullable":true}},{"name":"labels","in":"query","required":false,"schema":{"$ref":"#/components/schemas/Labels","nullable":true}},{"name":"before","in":"query","required":false,"schema":{"$ref":"#/components/schemas/Before","nullable":true}},{"name":"after","in":"query","required":false,"schema":{"$ref":"#/components/schemas/After","nullable":true}},{"name":"ascending","in":"query","required":false,"schema":{"$ref":"#/components/schemas/Ascending","nullable":true}},{"name":"include_spam","in":"query","required":false,"schema":{"$ref":"#/components/schemas/IncludeSpam","nullable":true}},{"name":"include_blocked","in":"query","required":false,"schema":{"$ref":"#/components/schemas/IncludeBlocked","nullable":true}},{"name":"include_unauthenticated","in":"query","required":false,"schema":{"$ref":"#/components/schemas/IncludeUnauthenticated","nullable":true}},{"name":"include_trash","in":"query","required":false,"schema":{"$ref":"#/components/schemas/IncludeTrash","nullable":true}},{"name":"from","in":"query","description":"Filter to messages whose sender contains this value (substring match). Repeatable; all values must match.","required":false,"schema":{"type":"array","items":{"type":"string"},"nullable":true}},{"name":"to","in":"query","description":"Filter to messages whose recipients (to, cc, or bcc) contain this value (substring match). Repeatable; all values must match.","required":false,"schema":{"type":"array","items":{"type":"string"},"nullable":true}},{"name":"subject","in":"query","description":"Filter to messages whose subject contains this value (substring match). Repeatable; all values must match.","required":false,"schema":{"type":"array","items":{"type":"string"},"nullable":true}}],"responses":{"200":{"description":"","content":{"application/json":{"schema":{"$ref":"#/components/schemas/ListMessagesResponse"}}}},"404":{"description":"","content":{"application/json":{"schema":{"$ref":"#/components/schemas/ErrorResponse"}}}}},"summary":"List Messages","servers":[{"url":"https://api.agentmail.to"},{"url":"https://x402.api.agentmail.to"},{"url":"https://mpp.api.agentmail.to"},{"url":"https://api.agentmail.eu"}],"security":[{"BearerAuth":[]},{"TokenAuth":[]}],"x-fern-sdk-group-name":["inboxes","messages"],"x-fern-sdk-method-name":"list"}},"/v0/inboxes/{inbox_id}/messages/search":{"get":{"description":"Full-text search across messages in the inbox, ranked by relevance. The\nquery is matched against the sender, recipients, and subject (substring)\nand the message body (tokenized full text). Spam, trash, blocked, and\nunauthenticated messages are always excluded. `limit` cannot exceed 100.","operationId":"inboxes_messages_search","tags":["InboxesMessages"],"parameters":[{"name":"inbox_id","in":"path","required":true,"schema":{"$ref":"#/components/schemas/inboxesInboxId"}},{"name":"q","in":"query","required":true,"schema":{"$ref":"#/components/schemas/Query"}},{"name":"limit","in":"query","required":false,"schema":{"$ref":"#/components/schemas/Limit","nullable":true}},{"name":"page_token","in":"query","required":false,"schema":{"$ref":"#/components/schemas/PageToken","nullable":true}},{"name":"before","in":"query","required":false,"schema":{"$ref":"#/components/schemas/Before","nullable":true}},{"name":"after","in":"query","required":false,"schema":{"$ref":"#/components/schemas/After","nullable":true}}],"responses":{"200":{"description":"","content":{"application/json":{"schema":{"$ref":"#/components/schemas/SearchMessagesResponse"}}}},"400":{"description":"","content":{"application/json":{"schema":{"$ref":"#/components/schemas/ValidationErrorResponse"}}}},"404":{"description":"","content":{"application/json":{"schema":{"$ref":"#/components/schemas/ErrorResponse"}}}}},"summary":"Search Messages","servers":[{"url":"https://api.agentmail.to"},{"url":"https://x402.api.agentmail.to"},{"url":"https://mpp.api.agentmail.to"},{"url":"https://api.agentmail.eu"}],"security":[{"BearerAuth":[]},{"TokenAuth":[]}],"x-fern-sdk-group-name":["inboxes","messages"],"x-fern-sdk-method-name":"search"}},"/v0/inboxes/{inbox_id}/messages/{message_id}":{"get":{"description":"**CLI:**\n```bash\nagentmail inboxes messages get --inbox-id --message-id \n```","operationId":"inboxes_messages_get","tags":["InboxesMessages"],"parameters":[{"name":"inbox_id","in":"path","required":true,"schema":{"$ref":"#/components/schemas/inboxesInboxId"}},{"name":"message_id","in":"path","required":true,"schema":{"$ref":"#/components/schemas/MessageId"}}],"responses":{"200":{"description":"","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Message"}}}},"404":{"description":"","content":{"application/json":{"schema":{"$ref":"#/components/schemas/ErrorResponse"}}}}},"summary":"Get Message","servers":[{"url":"https://api.agentmail.to"},{"url":"https://x402.api.agentmail.to"},{"url":"https://mpp.api.agentmail.to"},{"url":"https://api.agentmail.eu"}],"security":[{"BearerAuth":[]},{"TokenAuth":[]}],"x-fern-sdk-group-name":["inboxes","messages"],"x-fern-sdk-method-name":"get"},"patch":{"description":"**CLI:**\n```bash\nagentmail inboxes messages update --inbox-id --message-id --add-labels read --remove-labels unread\n```","operationId":"inboxes_messages_update","tags":["InboxesMessages"],"parameters":[{"name":"inbox_id","in":"path","required":true,"schema":{"$ref":"#/components/schemas/inboxesInboxId"}},{"name":"message_id","in":"path","required":true,"schema":{"$ref":"#/components/schemas/MessageId"}}],"responses":{"200":{"description":"","content":{"application/json":{"schema":{"$ref":"#/components/schemas/UpdateMessageResponse"}}}},"400":{"description":"","content":{"application/json":{"schema":{"$ref":"#/components/schemas/ValidationErrorResponse"}}}},"404":{"description":"","content":{"application/json":{"schema":{"$ref":"#/components/schemas/ErrorResponse"}}}}},"summary":"Update Message","servers":[{"url":"https://api.agentmail.to"},{"url":"https://x402.api.agentmail.to"},{"url":"https://mpp.api.agentmail.to"},{"url":"https://api.agentmail.eu"}],"security":[{"BearerAuth":[]},{"TokenAuth":[]}],"requestBody":{"required":true,"content":{"application/json":{"schema":{"$ref":"#/components/schemas/UpdateMessageRequest"}}}},"x-fern-sdk-group-name":["inboxes","messages"],"x-fern-sdk-method-name":"update"},"delete":{"description":"Permanently deletes a message.\n\n**CLI:**\n```bash\nagentmail inboxes messages delete --inbox-id --message-id \n```","operationId":"inboxes_messages_delete","tags":["InboxesMessages"],"parameters":[{"name":"inbox_id","in":"path","required":true,"schema":{"$ref":"#/components/schemas/inboxesInboxId"}},{"name":"message_id","in":"path","required":true,"schema":{"$ref":"#/components/schemas/MessageId"}}],"responses":{"204":{"description":""},"404":{"description":"","content":{"application/json":{"schema":{"$ref":"#/components/schemas/ErrorResponse"}}}}},"summary":"Delete Message","servers":[{"url":"https://api.agentmail.to"},{"url":"https://x402.api.agentmail.to"},{"url":"https://mpp.api.agentmail.to"},{"url":"https://api.agentmail.eu"}],"security":[{"BearerAuth":[]},{"TokenAuth":[]}],"x-fern-sdk-group-name":["inboxes","messages"],"x-fern-sdk-method-name":"delete"}},"/v0/inboxes/{inbox_id}/messages/batch-get":{"post":{"description":"Fetch metadata for up to 500 messages in one request. Missing or\nrestricted IDs are silently omitted; compare `count` against `limit`\nto detect misses.\n\n**CLI:**\n```bash\nagentmail inboxes messages batch-get --inbox-id --message-ids --message-ids \n```","operationId":"inboxes_messages_batchGet","tags":["InboxesMessages"],"parameters":[{"name":"inbox_id","in":"path","required":true,"schema":{"$ref":"#/components/schemas/inboxesInboxId"}}],"responses":{"200":{"description":"","content":{"application/json":{"schema":{"$ref":"#/components/schemas/BatchGetMessagesResponse"}}}},"400":{"description":"","content":{"application/json":{"schema":{"$ref":"#/components/schemas/ValidationErrorResponse"}}}}},"summary":"Batch Get Messages","servers":[{"url":"https://api.agentmail.to"},{"url":"https://x402.api.agentmail.to"},{"url":"https://mpp.api.agentmail.to"},{"url":"https://api.agentmail.eu"}],"security":[{"BearerAuth":[]},{"TokenAuth":[]}],"requestBody":{"required":true,"content":{"application/json":{"schema":{"$ref":"#/components/schemas/BatchGetMessagesRequest"}}}},"x-fern-sdk-group-name":["inboxes","messages"],"x-fern-sdk-method-name":"batch-get"}},"/v0/inboxes/{inbox_id}/messages/batch-update":{"post":{"description":"Apply one label change to up to 50 messages in a single request. The\nsame add_labels and remove_labels apply to every message id, and at\nleast one of them must be provided. The update is atomic: either all\nresolved messages are updated or none are. Missing or restricted ids\nare silently excluded; compare `count` against `limit` to detect\nexclusions.\n\n**CLI:**\n```bash\nagentmail inboxes messages batch-update --inbox-id --message-ids --message-ids --add-labels read --remove-labels unread\n```","operationId":"inboxes_messages_batchUpdate","tags":["InboxesMessages"],"parameters":[{"name":"inbox_id","in":"path","required":true,"schema":{"$ref":"#/components/schemas/inboxesInboxId"}}],"responses":{"200":{"description":"","content":{"application/json":{"schema":{"$ref":"#/components/schemas/BatchUpdateMessagesResponse"}}}},"400":{"description":"","content":{"application/json":{"schema":{"$ref":"#/components/schemas/ValidationErrorResponse"}}}}},"summary":"Batch Update Messages","servers":[{"url":"https://api.agentmail.to"},{"url":"https://x402.api.agentmail.to"},{"url":"https://mpp.api.agentmail.to"},{"url":"https://api.agentmail.eu"}],"security":[{"BearerAuth":[]},{"TokenAuth":[]}],"requestBody":{"required":true,"content":{"application/json":{"schema":{"$ref":"#/components/schemas/BatchUpdateMessagesRequest"}}}},"x-fern-sdk-group-name":["inboxes","messages"],"x-fern-sdk-method-name":"batch-update"}},"/v0/inboxes/{inbox_id}/messages/{message_id}/attachments/{attachment_id}":{"get":{"description":"**CLI:**\n```bash\nagentmail inboxes messages get-attachment --inbox-id --message-id --attachment-id \n```","operationId":"inboxes_messages_getAttachment","tags":["InboxesMessages"],"parameters":[{"name":"inbox_id","in":"path","required":true,"schema":{"$ref":"#/components/schemas/inboxesInboxId"}},{"name":"message_id","in":"path","required":true,"schema":{"$ref":"#/components/schemas/MessageId"}},{"name":"attachment_id","in":"path","required":true,"schema":{"$ref":"#/components/schemas/AttachmentId"}}],"responses":{"200":{"description":"","content":{"application/json":{"schema":{"$ref":"#/components/schemas/AttachmentResponse"}}}},"404":{"description":"","content":{"application/json":{"schema":{"$ref":"#/components/schemas/ErrorResponse"}}}}},"summary":"Get Attachment","servers":[{"url":"https://api.agentmail.to"},{"url":"https://x402.api.agentmail.to"},{"url":"https://mpp.api.agentmail.to"},{"url":"https://api.agentmail.eu"}],"security":[{"BearerAuth":[]},{"TokenAuth":[]}],"x-fern-sdk-group-name":["inboxes","messages"],"x-fern-sdk-method-name":"get-attachment"}},"/v0/inboxes/{inbox_id}/messages/{message_id}/raw":{"get":{"description":"**CLI:**\n```bash\nagentmail inboxes messages get-raw --inbox-id --message-id \n```","operationId":"inboxes_messages_getRaw","tags":["InboxesMessages"],"parameters":[{"name":"inbox_id","in":"path","required":true,"schema":{"$ref":"#/components/schemas/inboxesInboxId"}},{"name":"message_id","in":"path","required":true,"schema":{"$ref":"#/components/schemas/MessageId"}}],"responses":{"200":{"description":"","content":{"application/json":{"schema":{"$ref":"#/components/schemas/RawMessageResponse"}}}},"404":{"description":"","content":{"application/json":{"schema":{"$ref":"#/components/schemas/ErrorResponse"}}}}},"summary":"Get Raw Message","servers":[{"url":"https://api.agentmail.to"},{"url":"https://x402.api.agentmail.to"},{"url":"https://mpp.api.agentmail.to"},{"url":"https://api.agentmail.eu"}],"security":[{"BearerAuth":[]},{"TokenAuth":[]}],"x-fern-sdk-group-name":["inboxes","messages"],"x-fern-sdk-method-name":"get-raw"}},"/v0/inboxes/{inbox_id}/messages/send":{"post":{"description":"**CLI:**\n```bash\nagentmail inboxes messages send --inbox-id --to recipient@example.com --subject \"Hello\" --text \"Body\"\n```","operationId":"inboxes_messages_send","tags":["InboxesMessages"],"parameters":[{"name":"inbox_id","in":"path","required":true,"schema":{"$ref":"#/components/schemas/inboxesInboxId"}},{"name":"Idempotency-Key","in":"header","required":false,"description":"Unique key that makes a send idempotent. A retry carrying the same key returns the original message instead of sending a second email; reusing a key with a different request returns a 409 conflict. Keys expire 24 hours after the send completes.","schema":{"type":"string"}}],"responses":{"200":{"description":"","content":{"application/json":{"schema":{"$ref":"#/components/schemas/SendMessageResponse"}}}},"400":{"description":"","content":{"application/json":{"schema":{"$ref":"#/components/schemas/ValidationErrorResponse"}}}},"403":{"description":"","content":{"application/json":{"schema":{"$ref":"#/components/schemas/ErrorResponse"}}}},"404":{"description":"","content":{"application/json":{"schema":{"$ref":"#/components/schemas/ErrorResponse"}}}},"409":{"description":"","content":{"application/json":{"schema":{"$ref":"#/components/schemas/ErrorResponse"}}}}},"summary":"Send Message","servers":[{"url":"https://api.agentmail.to"},{"url":"https://x402.api.agentmail.to"},{"url":"https://mpp.api.agentmail.to"},{"url":"https://api.agentmail.eu"}],"security":[{"BearerAuth":[]},{"TokenAuth":[]}],"requestBody":{"required":true,"content":{"application/json":{"schema":{"$ref":"#/components/schemas/SendMessageRequest"}}}},"x-fern-idempotent":true,"x-fern-sdk-group-name":["inboxes","messages"],"x-fern-sdk-method-name":"send"}},"/v0/inboxes/{inbox_id}/messages/{message_id}/reply":{"post":{"description":"**CLI:**\n```bash\nagentmail inboxes messages reply --inbox-id --message-id --text \"Reply text\"\n```","operationId":"inboxes_messages_reply","tags":["InboxesMessages"],"parameters":[{"name":"inbox_id","in":"path","required":true,"schema":{"$ref":"#/components/schemas/inboxesInboxId"}},{"name":"message_id","in":"path","required":true,"schema":{"$ref":"#/components/schemas/MessageId"}},{"name":"Idempotency-Key","in":"header","required":false,"description":"Unique key that makes a send idempotent. A retry carrying the same key returns the original message instead of sending a second email; reusing a key with a different request returns a 409 conflict. Keys expire 24 hours after the send completes.","schema":{"type":"string"}}],"responses":{"200":{"description":"","content":{"application/json":{"schema":{"$ref":"#/components/schemas/SendMessageResponse"}}}},"400":{"description":"","content":{"application/json":{"schema":{"$ref":"#/components/schemas/ValidationErrorResponse"}}}},"403":{"description":"","content":{"application/json":{"schema":{"$ref":"#/components/schemas/ErrorResponse"}}}},"404":{"description":"","content":{"application/json":{"schema":{"$ref":"#/components/schemas/ErrorResponse"}}}},"409":{"description":"","content":{"application/json":{"schema":{"$ref":"#/components/schemas/ErrorResponse"}}}}},"summary":"Reply To Message","servers":[{"url":"https://api.agentmail.to"},{"url":"https://x402.api.agentmail.to"},{"url":"https://mpp.api.agentmail.to"},{"url":"https://api.agentmail.eu"}],"security":[{"BearerAuth":[]},{"TokenAuth":[]}],"requestBody":{"required":true,"content":{"application/json":{"schema":{"$ref":"#/components/schemas/ReplyToMessageRequest"}}}},"x-fern-idempotent":true,"x-fern-sdk-group-name":["inboxes","messages"],"x-fern-sdk-method-name":"reply"}},"/v0/inboxes/{inbox_id}/messages/{message_id}/reply-all":{"post":{"description":"**CLI:**\n```bash\nagentmail inboxes messages reply-all --inbox-id --message-id --text \"Reply text\"\n```","operationId":"inboxes_messages_reply-all","tags":["InboxesMessages"],"parameters":[{"name":"inbox_id","in":"path","required":true,"schema":{"$ref":"#/components/schemas/inboxesInboxId"}},{"name":"message_id","in":"path","required":true,"schema":{"$ref":"#/components/schemas/MessageId"}},{"name":"Idempotency-Key","in":"header","required":false,"description":"Unique key that makes a send idempotent. A retry carrying the same key returns the original message instead of sending a second email; reusing a key with a different request returns a 409 conflict. Keys expire 24 hours after the send completes.","schema":{"type":"string"}}],"responses":{"200":{"description":"","content":{"application/json":{"schema":{"$ref":"#/components/schemas/SendMessageResponse"}}}},"400":{"description":"","content":{"application/json":{"schema":{"$ref":"#/components/schemas/ValidationErrorResponse"}}}},"403":{"description":"","content":{"application/json":{"schema":{"$ref":"#/components/schemas/ErrorResponse"}}}},"404":{"description":"","content":{"application/json":{"schema":{"$ref":"#/components/schemas/ErrorResponse"}}}},"409":{"description":"","content":{"application/json":{"schema":{"$ref":"#/components/schemas/ErrorResponse"}}}}},"summary":"Reply All Message","servers":[{"url":"https://api.agentmail.to"},{"url":"https://x402.api.agentmail.to"},{"url":"https://mpp.api.agentmail.to"},{"url":"https://api.agentmail.eu"}],"security":[{"BearerAuth":[]},{"TokenAuth":[]}],"requestBody":{"required":true,"content":{"application/json":{"schema":{"$ref":"#/components/schemas/ReplyAllMessageRequest"}}}},"x-fern-idempotent":true,"x-fern-sdk-group-name":["inboxes","messages"],"x-fern-sdk-method-name":"reply-all"}},"/v0/inboxes/{inbox_id}/messages/{message_id}/forward":{"post":{"description":"**CLI:**\n```bash\nagentmail inboxes messages forward --inbox-id --message-id --to recipient@example.com\n```","operationId":"inboxes_messages_forward","tags":["InboxesMessages"],"parameters":[{"name":"inbox_id","in":"path","required":true,"schema":{"$ref":"#/components/schemas/inboxesInboxId"}},{"name":"message_id","in":"path","required":true,"schema":{"$ref":"#/components/schemas/MessageId"}},{"name":"Idempotency-Key","in":"header","required":false,"description":"Unique key that makes a send idempotent. A retry carrying the same key returns the original message instead of sending a second email; reusing a key with a different request returns a 409 conflict. Keys expire 24 hours after the send completes.","schema":{"type":"string"}}],"responses":{"200":{"description":"","content":{"application/json":{"schema":{"$ref":"#/components/schemas/SendMessageResponse"}}}},"400":{"description":"","content":{"application/json":{"schema":{"$ref":"#/components/schemas/ValidationErrorResponse"}}}},"403":{"description":"","content":{"application/json":{"schema":{"$ref":"#/components/schemas/ErrorResponse"}}}},"404":{"description":"","content":{"application/json":{"schema":{"$ref":"#/components/schemas/ErrorResponse"}}}},"409":{"description":"","content":{"application/json":{"schema":{"$ref":"#/components/schemas/ErrorResponse"}}}}},"summary":"Forward Message","servers":[{"url":"https://api.agentmail.to"},{"url":"https://x402.api.agentmail.to"},{"url":"https://mpp.api.agentmail.to"},{"url":"https://api.agentmail.eu"}],"security":[{"BearerAuth":[]},{"TokenAuth":[]}],"requestBody":{"required":true,"content":{"application/json":{"schema":{"$ref":"#/components/schemas/SendMessageRequest"}}}},"x-fern-idempotent":true,"x-fern-sdk-group-name":["inboxes","messages"],"x-fern-sdk-method-name":"forward"}},"/v0/inboxes/{inbox_id}/metrics/events":{"get":{"description":"Counts of email events (sent, delivered, bounced, etc.) over time for\nthe inbox. Defaults to the last 24 hours; `start` must be within the\nlast 90 days, and a future `end` is clamped to now. Omit `period` for\nindividual event counts, or set it to sum counts into buckets of that\nmany seconds.\n\n**CLI:**\n```bash\nagentmail inboxes metrics query-events --inbox-id \n```","operationId":"inboxes_metrics_queryEvents","tags":["InboxesMetrics"],"parameters":[{"name":"inbox_id","in":"path","required":true,"schema":{"$ref":"#/components/schemas/inboxesInboxId"}},{"name":"event_types","in":"query","required":false,"schema":{"$ref":"#/components/schemas/MetricEventTypes","nullable":true}},{"name":"start","in":"query","required":false,"schema":{"$ref":"#/components/schemas/Start","nullable":true}},{"name":"end","in":"query","required":false,"schema":{"$ref":"#/components/schemas/End","nullable":true}},{"name":"period","in":"query","required":false,"schema":{"$ref":"#/components/schemas/Period","nullable":true}},{"name":"limit","in":"query","required":false,"schema":{"$ref":"#/components/schemas/MetricLimit","nullable":true}},{"name":"descending","in":"query","required":false,"schema":{"$ref":"#/components/schemas/Descending","nullable":true}}],"responses":{"200":{"description":"","content":{"application/json":{"schema":{"$ref":"#/components/schemas/QueryMetricsResponse"}}}},"400":{"description":"","content":{"application/json":{"schema":{"$ref":"#/components/schemas/ValidationErrorResponse"}}}}},"summary":"Query Events","servers":[{"url":"https://api.agentmail.to"},{"url":"https://x402.api.agentmail.to"},{"url":"https://mpp.api.agentmail.to"},{"url":"https://api.agentmail.eu"}],"security":[{"BearerAuth":[]},{"TokenAuth":[]}],"x-fern-sdk-group-name":["inboxes","metrics"],"x-fern-sdk-method-name":"query-events"}},"/v0/inboxes/{inbox_id}/metrics/usage":{"get":{"description":"Cumulative usage series for the inbox. Each point is the running total\nof the usage type at that timestamp, not the change within the bucket.\nInbox-scoped queries carry `storage_bytes`, `message_count`, and\n`thread_count`; requested types that don't apply to the scope are\nignored. Defaults to the last 24 hours; `start` must be within the\nlast 90 days, and a future `end` is clamped to now. The range divided\nby `period` must not exceed 1000 buckets.","operationId":"inboxes_metrics_queryUsage","tags":["InboxesMetrics"],"parameters":[{"name":"inbox_id","in":"path","required":true,"schema":{"$ref":"#/components/schemas/inboxesInboxId"}},{"name":"usage_types","in":"query","required":false,"schema":{"$ref":"#/components/schemas/UsageTypes","nullable":true}},{"name":"start","in":"query","required":false,"schema":{"$ref":"#/components/schemas/Start","nullable":true}},{"name":"end","in":"query","required":false,"schema":{"$ref":"#/components/schemas/End","nullable":true}},{"name":"period","in":"query","required":false,"schema":{"$ref":"#/components/schemas/Period","nullable":true}},{"name":"limit","in":"query","required":false,"schema":{"$ref":"#/components/schemas/MetricLimit","nullable":true}},{"name":"descending","in":"query","required":false,"schema":{"$ref":"#/components/schemas/Descending","nullable":true}}],"responses":{"200":{"description":"","content":{"application/json":{"schema":{"$ref":"#/components/schemas/QueryUsageResponse"}}}},"400":{"description":"","content":{"application/json":{"schema":{"$ref":"#/components/schemas/ValidationErrorResponse"}}}}},"summary":"Query Usage","servers":[{"url":"https://api.agentmail.to"},{"url":"https://x402.api.agentmail.to"},{"url":"https://mpp.api.agentmail.to"},{"url":"https://api.agentmail.eu"}],"security":[{"BearerAuth":[]},{"TokenAuth":[]}],"x-fern-sdk-group-name":["inboxes","metrics"],"x-fern-sdk-method-name":"query-usage"}},"/v0/inboxes/{inbox_id}/threads":{"get":{"description":"Lists threads in the inbox, most recent first. Pass `senders`,\n`recipients`, or `subject` to filter by substring. Filtered requests are\nserved by search, which caps `limit` at 100. For relevance-ranked\nfull-text search, use `Search Threads`.\n\n**CLI:**\n```bash\nagentmail inboxes threads list --inbox-id \n```","operationId":"inboxes_threads_list","tags":["InboxesThreads"],"parameters":[{"name":"inbox_id","in":"path","required":true,"schema":{"$ref":"#/components/schemas/inboxesInboxId"}},{"name":"limit","in":"query","required":false,"schema":{"$ref":"#/components/schemas/Limit","nullable":true}},{"name":"page_token","in":"query","required":false,"schema":{"$ref":"#/components/schemas/PageToken","nullable":true}},{"name":"labels","in":"query","required":false,"schema":{"$ref":"#/components/schemas/Labels","nullable":true}},{"name":"before","in":"query","required":false,"schema":{"$ref":"#/components/schemas/Before","nullable":true}},{"name":"after","in":"query","required":false,"schema":{"$ref":"#/components/schemas/After","nullable":true}},{"name":"ascending","in":"query","required":false,"schema":{"$ref":"#/components/schemas/Ascending","nullable":true}},{"name":"include_spam","in":"query","required":false,"schema":{"$ref":"#/components/schemas/IncludeSpam","nullable":true}},{"name":"include_blocked","in":"query","required":false,"schema":{"$ref":"#/components/schemas/IncludeBlocked","nullable":true}},{"name":"include_unauthenticated","in":"query","required":false,"schema":{"$ref":"#/components/schemas/IncludeUnauthenticated","nullable":true}},{"name":"include_trash","in":"query","required":false,"schema":{"$ref":"#/components/schemas/IncludeTrash","nullable":true}},{"name":"senders","in":"query","description":"Filter to threads whose senders contain this value (substring match). Repeatable; all values must match.","required":false,"schema":{"type":"array","items":{"type":"string"},"nullable":true}},{"name":"recipients","in":"query","description":"Filter to threads whose recipients contain this value (substring match). Repeatable; all values must match.","required":false,"schema":{"type":"array","items":{"type":"string"},"nullable":true}},{"name":"subject","in":"query","description":"Filter to threads whose subject contains this value (substring match). Repeatable; all values must match.","required":false,"schema":{"type":"array","items":{"type":"string"},"nullable":true}}],"responses":{"200":{"description":"","content":{"application/json":{"schema":{"$ref":"#/components/schemas/ListThreadsResponse"}}}},"404":{"description":"","content":{"application/json":{"schema":{"$ref":"#/components/schemas/ErrorResponse"}}}}},"summary":"List Threads","servers":[{"url":"https://api.agentmail.to"},{"url":"https://x402.api.agentmail.to"},{"url":"https://mpp.api.agentmail.to"},{"url":"https://api.agentmail.eu"}],"security":[{"BearerAuth":[]},{"TokenAuth":[]}],"x-fern-sdk-group-name":["inboxes","threads"],"x-fern-sdk-method-name":"list"}},"/v0/inboxes/{inbox_id}/threads/search":{"get":{"description":"Full-text search across threads in the inbox, ranked by relevance. The\nquery is matched against senders, recipients, and subject (substring)\nand the message body (tokenized full text). Spam, trash, blocked, and\nunauthenticated threads are always excluded. `limit` cannot exceed 100.","operationId":"inboxes_threads_search","tags":["InboxesThreads"],"parameters":[{"name":"inbox_id","in":"path","required":true,"schema":{"$ref":"#/components/schemas/inboxesInboxId"}},{"name":"q","in":"query","required":true,"schema":{"$ref":"#/components/schemas/Query"}},{"name":"limit","in":"query","required":false,"schema":{"$ref":"#/components/schemas/Limit","nullable":true}},{"name":"page_token","in":"query","required":false,"schema":{"$ref":"#/components/schemas/PageToken","nullable":true}},{"name":"before","in":"query","required":false,"schema":{"$ref":"#/components/schemas/Before","nullable":true}},{"name":"after","in":"query","required":false,"schema":{"$ref":"#/components/schemas/After","nullable":true}}],"responses":{"200":{"description":"","content":{"application/json":{"schema":{"$ref":"#/components/schemas/SearchThreadsResponse"}}}},"400":{"description":"","content":{"application/json":{"schema":{"$ref":"#/components/schemas/ValidationErrorResponse"}}}},"404":{"description":"","content":{"application/json":{"schema":{"$ref":"#/components/schemas/ErrorResponse"}}}}},"summary":"Search Threads","servers":[{"url":"https://api.agentmail.to"},{"url":"https://x402.api.agentmail.to"},{"url":"https://mpp.api.agentmail.to"},{"url":"https://api.agentmail.eu"}],"security":[{"BearerAuth":[]},{"TokenAuth":[]}],"x-fern-sdk-group-name":["inboxes","threads"],"x-fern-sdk-method-name":"search"}},"/v0/inboxes/{inbox_id}/threads/{thread_id}":{"get":{"description":"**CLI:**\n```bash\nagentmail inboxes threads get --inbox-id --thread-id \n```","operationId":"inboxes_threads_get","tags":["InboxesThreads"],"parameters":[{"name":"inbox_id","in":"path","required":true,"schema":{"$ref":"#/components/schemas/inboxesInboxId"}},{"name":"thread_id","in":"path","required":true,"schema":{"$ref":"#/components/schemas/ThreadId"}}],"responses":{"200":{"description":"","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Thread"}}}},"404":{"description":"","content":{"application/json":{"schema":{"$ref":"#/components/schemas/ErrorResponse"}}}}},"summary":"Get Thread","servers":[{"url":"https://api.agentmail.to"},{"url":"https://x402.api.agentmail.to"},{"url":"https://mpp.api.agentmail.to"},{"url":"https://api.agentmail.eu"}],"security":[{"BearerAuth":[]},{"TokenAuth":[]}],"x-fern-sdk-group-name":["inboxes","threads"],"x-fern-sdk-method-name":"get"},"patch":{"description":"Updates thread labels. Cannot add or remove system labels (sent, received, bounced, etc.). Rejects requests with a `422` for threads with 100 or more messages.","operationId":"inboxes_threads_update","tags":["InboxesThreads"],"parameters":[{"name":"inbox_id","in":"path","required":true,"schema":{"$ref":"#/components/schemas/inboxesInboxId"}},{"name":"thread_id","in":"path","required":true,"schema":{"$ref":"#/components/schemas/ThreadId"}}],"responses":{"200":{"description":"","content":{"application/json":{"schema":{"$ref":"#/components/schemas/UpdateThreadResponse"}}}},"400":{"description":"","content":{"application/json":{"schema":{"$ref":"#/components/schemas/ValidationErrorResponse"}}}},"404":{"description":"","content":{"application/json":{"schema":{"$ref":"#/components/schemas/ErrorResponse"}}}},"422":{"description":"","content":{"application/json":{"schema":{"$ref":"#/components/schemas/ErrorResponse"}}}}},"summary":"Update Thread","servers":[{"url":"https://api.agentmail.to"},{"url":"https://x402.api.agentmail.to"},{"url":"https://mpp.api.agentmail.to"},{"url":"https://api.agentmail.eu"}],"security":[{"BearerAuth":[]},{"TokenAuth":[]}],"requestBody":{"required":true,"content":{"application/json":{"schema":{"$ref":"#/components/schemas/UpdateThreadRequest"}}}},"x-fern-sdk-group-name":["inboxes","threads"],"x-fern-sdk-method-name":"update"},"delete":{"description":"Permanently deletes a thread and all of its messages.\n\n**CLI:**\n```bash\nagentmail inboxes threads delete --inbox-id --thread-id \n```","operationId":"inboxes_threads_delete","tags":["InboxesThreads"],"parameters":[{"name":"inbox_id","in":"path","required":true,"schema":{"$ref":"#/components/schemas/inboxesInboxId"}},{"name":"thread_id","in":"path","required":true,"schema":{"$ref":"#/components/schemas/ThreadId"}}],"responses":{"204":{"description":""},"404":{"description":"","content":{"application/json":{"schema":{"$ref":"#/components/schemas/ErrorResponse"}}}}},"summary":"Delete Thread","servers":[{"url":"https://api.agentmail.to"},{"url":"https://x402.api.agentmail.to"},{"url":"https://mpp.api.agentmail.to"},{"url":"https://api.agentmail.eu"}],"security":[{"BearerAuth":[]},{"TokenAuth":[]}],"x-fern-sdk-group-name":["inboxes","threads"],"x-fern-sdk-method-name":"delete"}},"/v0/inboxes/{inbox_id}/threads/{thread_id}/attachments/{attachment_id}":{"get":{"description":"**CLI:**\n```bash\nagentmail inboxes threads get-attachment --inbox-id --thread-id --attachment-id \n```","operationId":"inboxes_threads_getAttachment","tags":["InboxesThreads"],"parameters":[{"name":"inbox_id","in":"path","required":true,"schema":{"$ref":"#/components/schemas/inboxesInboxId"}},{"name":"thread_id","in":"path","required":true,"schema":{"$ref":"#/components/schemas/ThreadId"}},{"name":"attachment_id","in":"path","required":true,"schema":{"$ref":"#/components/schemas/AttachmentId"}}],"responses":{"200":{"description":"","content":{"application/json":{"schema":{"$ref":"#/components/schemas/AttachmentResponse"}}}},"404":{"description":"","content":{"application/json":{"schema":{"$ref":"#/components/schemas/ErrorResponse"}}}}},"summary":"Get Attachment","servers":[{"url":"https://api.agentmail.to"},{"url":"https://x402.api.agentmail.to"},{"url":"https://mpp.api.agentmail.to"},{"url":"https://api.agentmail.eu"}],"security":[{"BearerAuth":[]},{"TokenAuth":[]}],"x-fern-sdk-group-name":["inboxes","threads"],"x-fern-sdk-method-name":"get-attachment"}},"/v0/inboxes/{inbox_id}/webhooks":{"get":{"description":"**CLI:**\n```bash\nagentmail inboxes webhooks list --inbox-id \n```","operationId":"inboxes_webhooks_list","tags":["InboxesWebhooks"],"parameters":[{"name":"inbox_id","in":"path","required":true,"schema":{"$ref":"#/components/schemas/inboxesInboxId"}},{"name":"limit","in":"query","required":false,"schema":{"$ref":"#/components/schemas/Limit","nullable":true}},{"name":"page_token","in":"query","required":false,"schema":{"$ref":"#/components/schemas/PageToken","nullable":true}},{"name":"ascending","in":"query","required":false,"schema":{"$ref":"#/components/schemas/Ascending","nullable":true}}],"responses":{"200":{"description":"","content":{"application/json":{"schema":{"$ref":"#/components/schemas/webhooksListWebhooksResponse"}}}}},"summary":"List Webhooks","servers":[{"url":"https://api.agentmail.to"},{"url":"https://x402.api.agentmail.to"},{"url":"https://mpp.api.agentmail.to"},{"url":"https://api.agentmail.eu"}],"security":[{"BearerAuth":[]},{"TokenAuth":[]}],"x-fern-sdk-group-name":["inboxes","webhooks"],"x-fern-sdk-method-name":"list"},"post":{"description":"Create a webhook scoped to this inbox.\n\n**CLI:**\n```bash\nagentmail inboxes webhooks create --inbox-id --url https://example.com/webhook --event-types message.received\n```","operationId":"inboxes_webhooks_create","tags":["InboxesWebhooks"],"parameters":[{"name":"inbox_id","in":"path","required":true,"schema":{"$ref":"#/components/schemas/inboxesInboxId"}}],"responses":{"200":{"description":"","content":{"application/json":{"schema":{"$ref":"#/components/schemas/webhooksWebhook"}}}},"400":{"description":"","content":{"application/json":{"schema":{"$ref":"#/components/schemas/ValidationErrorResponse"}}}}},"summary":"Create Webhook","servers":[{"url":"https://api.agentmail.to"},{"url":"https://x402.api.agentmail.to"},{"url":"https://mpp.api.agentmail.to"},{"url":"https://api.agentmail.eu"}],"security":[{"BearerAuth":[]},{"TokenAuth":[]}],"requestBody":{"required":true,"content":{"application/json":{"schema":{"$ref":"#/components/schemas/webhooksCreateInboxWebhookRequest"}}}},"x-fern-sdk-group-name":["inboxes","webhooks"],"x-fern-sdk-method-name":"create"}},"/v0/inboxes/{inbox_id}/webhooks/{webhook_id}":{"get":{"description":"**CLI:**\n```bash\nagentmail inboxes webhooks get --inbox-id --webhook-id \n```","operationId":"inboxes_webhooks_get","tags":["InboxesWebhooks"],"parameters":[{"name":"inbox_id","in":"path","required":true,"schema":{"$ref":"#/components/schemas/inboxesInboxId"}},{"name":"webhook_id","in":"path","required":true,"schema":{"$ref":"#/components/schemas/webhooksWebhookId"}}],"responses":{"200":{"description":"","content":{"application/json":{"schema":{"$ref":"#/components/schemas/webhooksWebhook"}}}},"404":{"description":"","content":{"application/json":{"schema":{"$ref":"#/components/schemas/ErrorResponse"}}}}},"summary":"Get Webhook","servers":[{"url":"https://api.agentmail.to"},{"url":"https://x402.api.agentmail.to"},{"url":"https://mpp.api.agentmail.to"},{"url":"https://api.agentmail.eu"}],"security":[{"BearerAuth":[]},{"TokenAuth":[]}],"x-fern-sdk-group-name":["inboxes","webhooks"],"x-fern-sdk-method-name":"get"},"patch":{"description":"**CLI:**\n```bash\nagentmail inboxes webhooks update --inbox-id --webhook-id --event-types message.received\n```","operationId":"inboxes_webhooks_update","tags":["InboxesWebhooks"],"parameters":[{"name":"inbox_id","in":"path","required":true,"schema":{"$ref":"#/components/schemas/inboxesInboxId"}},{"name":"webhook_id","in":"path","required":true,"schema":{"$ref":"#/components/schemas/webhooksWebhookId"}}],"responses":{"200":{"description":"","content":{"application/json":{"schema":{"$ref":"#/components/schemas/webhooksWebhook"}}}},"400":{"description":"","content":{"application/json":{"schema":{"$ref":"#/components/schemas/ValidationErrorResponse"}}}},"404":{"description":"","content":{"application/json":{"schema":{"$ref":"#/components/schemas/ErrorResponse"}}}}},"summary":"Update Webhook","servers":[{"url":"https://api.agentmail.to"},{"url":"https://x402.api.agentmail.to"},{"url":"https://mpp.api.agentmail.to"},{"url":"https://api.agentmail.eu"}],"security":[{"BearerAuth":[]},{"TokenAuth":[]}],"requestBody":{"required":true,"content":{"application/json":{"schema":{"$ref":"#/components/schemas/webhooksUpdateInboxWebhookRequest"}}}},"x-fern-sdk-group-name":["inboxes","webhooks"],"x-fern-sdk-method-name":"update"},"delete":{"description":"**CLI:**\n```bash\nagentmail inboxes webhooks delete --inbox-id --webhook-id \n```","operationId":"inboxes_webhooks_delete","tags":["InboxesWebhooks"],"parameters":[{"name":"inbox_id","in":"path","required":true,"schema":{"$ref":"#/components/schemas/inboxesInboxId"}},{"name":"webhook_id","in":"path","required":true,"schema":{"$ref":"#/components/schemas/webhooksWebhookId"}}],"responses":{"204":{"description":""},"404":{"description":"","content":{"application/json":{"schema":{"$ref":"#/components/schemas/ErrorResponse"}}}}},"summary":"Delete Webhook","servers":[{"url":"https://api.agentmail.to"},{"url":"https://x402.api.agentmail.to"},{"url":"https://mpp.api.agentmail.to"},{"url":"https://api.agentmail.eu"}],"security":[{"BearerAuth":[]},{"TokenAuth":[]}],"x-fern-sdk-group-name":["inboxes","webhooks"],"x-fern-sdk-method-name":"delete"}},"/v0/inboxes/{inbox_id}/webhooks/{webhook_id}/headers":{"get":{"description":"List the names of custom HTTP headers included with deliveries to this inbox-scoped webhook.\nHeader values are write-only and are never returned.","operationId":"inboxes_webhooks_getHeaders","tags":["InboxesWebhooks"],"parameters":[{"name":"inbox_id","in":"path","required":true,"schema":{"$ref":"#/components/schemas/inboxesInboxId"}},{"name":"webhook_id","in":"path","required":true,"schema":{"$ref":"#/components/schemas/webhooksWebhookId"}}],"responses":{"200":{"description":"","content":{"application/json":{"schema":{"$ref":"#/components/schemas/webhooksWebhookHeaderNamesResponse"}}}},"404":{"description":"","content":{"application/json":{"schema":{"$ref":"#/components/schemas/ErrorResponse"}}}}},"summary":"Get Webhook Headers","servers":[{"url":"https://api.agentmail.to"},{"url":"https://x402.api.agentmail.to"},{"url":"https://mpp.api.agentmail.to"},{"url":"https://api.agentmail.eu"}],"security":[{"BearerAuth":[]},{"TokenAuth":[]}],"x-fern-sdk-group-name":["inboxes","webhooks"],"x-fern-sdk-method-name":"get-headers"},"patch":{"description":"Atomically set, replace, or remove custom HTTP headers included with deliveries to this\ninbox-scoped webhook. Header values remain write-only.","operationId":"inboxes_webhooks_updateHeaders","tags":["InboxesWebhooks"],"parameters":[{"name":"inbox_id","in":"path","required":true,"schema":{"$ref":"#/components/schemas/inboxesInboxId"}},{"name":"webhook_id","in":"path","required":true,"schema":{"$ref":"#/components/schemas/webhooksWebhookId"}}],"responses":{"204":{"description":""},"400":{"description":"","content":{"application/json":{"schema":{"$ref":"#/components/schemas/ValidationErrorResponse"}}}},"404":{"description":"","content":{"application/json":{"schema":{"$ref":"#/components/schemas/ErrorResponse"}}}}},"summary":"Update Webhook Headers","servers":[{"url":"https://api.agentmail.to"},{"url":"https://x402.api.agentmail.to"},{"url":"https://mpp.api.agentmail.to"},{"url":"https://api.agentmail.eu"}],"security":[{"BearerAuth":[]},{"TokenAuth":[]}],"requestBody":{"required":true,"content":{"application/json":{"schema":{"$ref":"#/components/schemas/webhooksUpdateWebhookHeadersRequest"}}}},"x-fern-sdk-group-name":["inboxes","webhooks"],"x-fern-sdk-method-name":"update-headers"}},"/v0/lists/{direction}/{type}":{"get":{"description":"**CLI:**\n```bash\nagentmail lists list --direction --type \n```","operationId":"lists_list","tags":["Lists"],"parameters":[{"name":"direction","in":"path","required":true,"schema":{"$ref":"#/components/schemas/Direction"}},{"name":"type","in":"path","required":true,"schema":{"$ref":"#/components/schemas/ListType"}},{"name":"limit","in":"query","required":false,"schema":{"$ref":"#/components/schemas/Limit","nullable":true}},{"name":"page_token","in":"query","required":false,"schema":{"$ref":"#/components/schemas/PageToken","nullable":true}}],"responses":{"200":{"description":"","content":{"application/json":{"schema":{"$ref":"#/components/schemas/ListListEntriesResponse"}}}}},"summary":"List Entries","servers":[{"url":"https://api.agentmail.to"},{"url":"https://x402.api.agentmail.to"},{"url":"https://mpp.api.agentmail.to"},{"url":"https://api.agentmail.eu"}],"security":[{"BearerAuth":[]},{"TokenAuth":[]}],"x-fern-sdk-group-name":["lists"],"x-fern-sdk-method-name":"list"},"post":{"description":"**CLI:**\n```bash\nagentmail lists create --direction --type --entry user@example.com\n```","operationId":"lists_create","tags":["Lists"],"parameters":[{"name":"direction","in":"path","required":true,"schema":{"$ref":"#/components/schemas/Direction"}},{"name":"type","in":"path","required":true,"schema":{"$ref":"#/components/schemas/ListType"}}],"responses":{"200":{"description":"","content":{"application/json":{"schema":{"$ref":"#/components/schemas/ListEntry"}}}},"400":{"description":"","content":{"application/json":{"schema":{"$ref":"#/components/schemas/ValidationErrorResponse"}}}}},"summary":"Create List Entry","servers":[{"url":"https://api.agentmail.to"},{"url":"https://x402.api.agentmail.to"},{"url":"https://mpp.api.agentmail.to"},{"url":"https://api.agentmail.eu"}],"security":[{"BearerAuth":[]},{"TokenAuth":[]}],"requestBody":{"required":true,"content":{"application/json":{"schema":{"$ref":"#/components/schemas/CreateListEntryRequest"}}}},"x-fern-sdk-group-name":["lists"],"x-fern-sdk-method-name":"create"}},"/v0/lists/{direction}/{type}/{entry}":{"get":{"description":"**CLI:**\n```bash\nagentmail lists get --direction --type --entry \n```","operationId":"lists_get","tags":["Lists"],"parameters":[{"name":"direction","in":"path","required":true,"schema":{"$ref":"#/components/schemas/Direction"}},{"name":"type","in":"path","required":true,"schema":{"$ref":"#/components/schemas/ListType"}},{"name":"entry","in":"path","description":"Email address or domain.","required":true,"schema":{"type":"string"}}],"responses":{"200":{"description":"","content":{"application/json":{"schema":{"$ref":"#/components/schemas/ListEntry"}}}},"404":{"description":"","content":{"application/json":{"schema":{"$ref":"#/components/schemas/ErrorResponse"}}}}},"summary":"Get List Entry","servers":[{"url":"https://api.agentmail.to"},{"url":"https://x402.api.agentmail.to"},{"url":"https://mpp.api.agentmail.to"},{"url":"https://api.agentmail.eu"}],"security":[{"BearerAuth":[]},{"TokenAuth":[]}],"x-fern-sdk-group-name":["lists"],"x-fern-sdk-method-name":"get"},"delete":{"description":"**CLI:**\n```bash\nagentmail lists delete --direction --type --entry \n```","operationId":"lists_delete","tags":["Lists"],"parameters":[{"name":"direction","in":"path","required":true,"schema":{"$ref":"#/components/schemas/Direction"}},{"name":"type","in":"path","required":true,"schema":{"$ref":"#/components/schemas/ListType"}},{"name":"entry","in":"path","description":"Email address or domain.","required":true,"schema":{"type":"string"}}],"responses":{"204":{"description":""},"404":{"description":"","content":{"application/json":{"schema":{"$ref":"#/components/schemas/ErrorResponse"}}}}},"summary":"Delete List Entry","servers":[{"url":"https://api.agentmail.to"},{"url":"https://x402.api.agentmail.to"},{"url":"https://mpp.api.agentmail.to"},{"url":"https://api.agentmail.eu"}],"security":[{"BearerAuth":[]},{"TokenAuth":[]}],"x-fern-sdk-group-name":["lists"],"x-fern-sdk-method-name":"delete"}},"/v0/metrics/events":{"get":{"description":"Counts of email events (sent, delivered, bounced, etc.) over time for\nthe organization. Defaults to the last 24 hours; `start` must be within\nthe last 90 days, and a future `end` is clamped to now. Omit `period`\nfor individual event counts, or set it to sum counts into buckets of\nthat many seconds.\n\n**CLI:**\n```bash\nagentmail metrics query-events\n```","operationId":"metrics_queryEvents","tags":["Metrics"],"parameters":[{"name":"event_types","in":"query","required":false,"schema":{"$ref":"#/components/schemas/MetricEventTypes","nullable":true}},{"name":"start","in":"query","required":false,"schema":{"$ref":"#/components/schemas/Start","nullable":true}},{"name":"end","in":"query","required":false,"schema":{"$ref":"#/components/schemas/End","nullable":true}},{"name":"period","in":"query","required":false,"schema":{"$ref":"#/components/schemas/Period","nullable":true}},{"name":"limit","in":"query","required":false,"schema":{"$ref":"#/components/schemas/MetricLimit","nullable":true}},{"name":"descending","in":"query","required":false,"schema":{"$ref":"#/components/schemas/Descending","nullable":true}}],"responses":{"200":{"description":"","content":{"application/json":{"schema":{"$ref":"#/components/schemas/QueryMetricsResponse"}}}},"400":{"description":"","content":{"application/json":{"schema":{"$ref":"#/components/schemas/ValidationErrorResponse"}}}}},"summary":"Query Events","servers":[{"url":"https://api.agentmail.to"},{"url":"https://x402.api.agentmail.to"},{"url":"https://mpp.api.agentmail.to"},{"url":"https://api.agentmail.eu"}],"security":[{"BearerAuth":[]},{"TokenAuth":[]}],"x-fern-sdk-group-name":["metrics"],"x-fern-sdk-method-name":"query-events"}},"/v0/metrics/usage":{"get":{"description":"Cumulative usage series for the organization. Each point is the running\ntotal of the usage type at that timestamp, not the change within the\nbucket. Defaults to the last 24 hours; `start` must be within the last\n90 days, and a future `end` is clamped to now. The range divided by\n`period` must not exceed 1000 buckets.","operationId":"metrics_queryUsage","tags":["Metrics"],"parameters":[{"name":"usage_types","in":"query","required":false,"schema":{"$ref":"#/components/schemas/UsageTypes","nullable":true}},{"name":"start","in":"query","required":false,"schema":{"$ref":"#/components/schemas/Start","nullable":true}},{"name":"end","in":"query","required":false,"schema":{"$ref":"#/components/schemas/End","nullable":true}},{"name":"period","in":"query","required":false,"schema":{"$ref":"#/components/schemas/Period","nullable":true}},{"name":"limit","in":"query","required":false,"schema":{"$ref":"#/components/schemas/MetricLimit","nullable":true}},{"name":"descending","in":"query","required":false,"schema":{"$ref":"#/components/schemas/Descending","nullable":true}}],"responses":{"200":{"description":"","content":{"application/json":{"schema":{"$ref":"#/components/schemas/QueryUsageResponse"}}}},"400":{"description":"","content":{"application/json":{"schema":{"$ref":"#/components/schemas/ValidationErrorResponse"}}}}},"summary":"Query Usage","servers":[{"url":"https://api.agentmail.to"},{"url":"https://x402.api.agentmail.to"},{"url":"https://mpp.api.agentmail.to"},{"url":"https://api.agentmail.eu"}],"security":[{"BearerAuth":[]},{"TokenAuth":[]}],"x-fern-sdk-group-name":["metrics"],"x-fern-sdk-method-name":"query-usage"}},"/v0/organizations":{"get":{"description":"Returns the organization for the authenticated API key (usage limits, counts, and billing metadata).\n\n**CLI:**\n```bash\nagentmail organizations get\n```","operationId":"organizations_get","tags":["Organizations"],"parameters":[],"responses":{"200":{"description":"","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Organization"}}}}},"summary":"Get Organization","servers":[{"url":"https://api.agentmail.to"},{"url":"https://x402.api.agentmail.to"},{"url":"https://mpp.api.agentmail.to"},{"url":"https://api.agentmail.eu"}],"security":[{"BearerAuth":[]},{"TokenAuth":[]}],"x-fern-sdk-group-name":["organizations"],"x-fern-sdk-method-name":"get"}},"/v0/pods/{pod_id}/api-keys":{"get":{"description":"**CLI:**\n```bash\nagentmail pods api-keys list --pod-id \n```","operationId":"pods_apiKeys_list","tags":["PodsApiKeys"],"parameters":[{"name":"pod_id","in":"path","required":true,"schema":{"$ref":"#/components/schemas/podsPodId"}},{"name":"limit","in":"query","required":false,"schema":{"$ref":"#/components/schemas/Limit","nullable":true}},{"name":"page_token","in":"query","required":false,"schema":{"$ref":"#/components/schemas/PageToken","nullable":true}}],"responses":{"200":{"description":"","content":{"application/json":{"schema":{"$ref":"#/components/schemas/ListApiKeysResponse"}}}},"404":{"description":"","content":{"application/json":{"schema":{"$ref":"#/components/schemas/ErrorResponse"}}}}},"summary":"List API Keys","servers":[{"url":"https://api.agentmail.to"},{"url":"https://x402.api.agentmail.to"},{"url":"https://mpp.api.agentmail.to"},{"url":"https://api.agentmail.eu"}],"security":[{"BearerAuth":[]},{"TokenAuth":[]}],"x-fern-sdk-group-name":["pods","apiKeys"],"x-fern-sdk-method-name":"list"},"post":{"description":"**CLI:**\n```bash\nagentmail pods api-keys create --pod-id --name \"My Key\"\n```","operationId":"pods_apiKeys_create","tags":["PodsApiKeys"],"parameters":[{"name":"pod_id","in":"path","required":true,"schema":{"$ref":"#/components/schemas/podsPodId"}}],"responses":{"200":{"description":"","content":{"application/json":{"schema":{"$ref":"#/components/schemas/CreateApiKeyResponse"}}}},"400":{"description":"","content":{"application/json":{"schema":{"$ref":"#/components/schemas/ValidationErrorResponse"}}}},"404":{"description":"","content":{"application/json":{"schema":{"$ref":"#/components/schemas/ErrorResponse"}}}}},"summary":"Create API Key","servers":[{"url":"https://api.agentmail.to"},{"url":"https://x402.api.agentmail.to"},{"url":"https://mpp.api.agentmail.to"},{"url":"https://api.agentmail.eu"}],"security":[{"BearerAuth":[]},{"TokenAuth":[]}],"requestBody":{"required":true,"content":{"application/json":{"schema":{"$ref":"#/components/schemas/CreateApiKeyRequest"}}}},"x-fern-sdk-group-name":["pods","apiKeys"],"x-fern-sdk-method-name":"create"}},"/v0/pods/{pod_id}/api-keys/{api_key_id}":{"delete":{"description":"**CLI:**\n```bash\nagentmail pods api-keys delete --pod-id --api-key-id \n```","operationId":"pods_apiKeys_delete","tags":["PodsApiKeys"],"parameters":[{"name":"pod_id","in":"path","required":true,"schema":{"$ref":"#/components/schemas/podsPodId"}},{"name":"api_key_id","in":"path","required":true,"schema":{"$ref":"#/components/schemas/ApiKeyId"}}],"responses":{"204":{"description":""},"404":{"description":"","content":{"application/json":{"schema":{"$ref":"#/components/schemas/ErrorResponse"}}}}},"summary":"Delete API Key","servers":[{"url":"https://api.agentmail.to"},{"url":"https://x402.api.agentmail.to"},{"url":"https://mpp.api.agentmail.to"},{"url":"https://api.agentmail.eu"}],"security":[{"BearerAuth":[]},{"TokenAuth":[]}],"x-fern-sdk-group-name":["pods","apiKeys"],"x-fern-sdk-method-name":"delete"}},"/v0/pods/{pod_id}/domains":{"get":{"description":"**CLI:**\n```bash\nagentmail pods domains list --pod-id \n```","operationId":"pods_domains_list","tags":["PodsDomains"],"parameters":[{"name":"pod_id","in":"path","required":true,"schema":{"$ref":"#/components/schemas/podsPodId"}},{"name":"limit","in":"query","required":false,"schema":{"$ref":"#/components/schemas/Limit","nullable":true}},{"name":"page_token","in":"query","required":false,"schema":{"$ref":"#/components/schemas/PageToken","nullable":true}},{"name":"ascending","in":"query","required":false,"schema":{"$ref":"#/components/schemas/Ascending","nullable":true}}],"responses":{"200":{"description":"","content":{"application/json":{"schema":{"$ref":"#/components/schemas/ListDomainsResponse"}}}},"404":{"description":"","content":{"application/json":{"schema":{"$ref":"#/components/schemas/ErrorResponse"}}}}},"summary":"List Domains","servers":[{"url":"https://api.agentmail.to"},{"url":"https://x402.api.agentmail.to"},{"url":"https://mpp.api.agentmail.to"},{"url":"https://api.agentmail.eu"}],"security":[{"BearerAuth":[]},{"TokenAuth":[]}],"x-fern-sdk-group-name":["pods","domains"],"x-fern-sdk-method-name":"list"},"post":{"description":"**CLI:**\n```bash\nagentmail pods domains create --pod-id --domain example.com\n```","operationId":"pods_domains_create","tags":["PodsDomains"],"parameters":[{"name":"pod_id","in":"path","required":true,"schema":{"$ref":"#/components/schemas/podsPodId"}}],"responses":{"200":{"description":"","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Domain"}}}},"400":{"description":"","content":{"application/json":{"schema":{"$ref":"#/components/schemas/ValidationErrorResponse"}}}}},"summary":"Create Domain","servers":[{"url":"https://api.agentmail.to"},{"url":"https://x402.api.agentmail.to"},{"url":"https://mpp.api.agentmail.to"},{"url":"https://api.agentmail.eu"}],"security":[{"BearerAuth":[]},{"TokenAuth":[]}],"requestBody":{"required":true,"content":{"application/json":{"schema":{"$ref":"#/components/schemas/CreateDomainRequest"}}}},"x-fern-sdk-group-name":["pods","domains"],"x-fern-sdk-method-name":"create"}},"/v0/pods/{pod_id}/domains/{domain_id}":{"get":{"description":"**CLI:**\n```bash\nagentmail pods domains get --pod-id --domain-id \n```","operationId":"pods_domains_get","tags":["PodsDomains"],"parameters":[{"name":"pod_id","in":"path","required":true,"schema":{"$ref":"#/components/schemas/podsPodId"}},{"name":"domain_id","in":"path","required":true,"schema":{"$ref":"#/components/schemas/DomainId"}}],"responses":{"200":{"description":"","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Domain"}}}},"404":{"description":"","content":{"application/json":{"schema":{"$ref":"#/components/schemas/ErrorResponse"}}}}},"summary":"Get Domain","servers":[{"url":"https://api.agentmail.to"},{"url":"https://x402.api.agentmail.to"},{"url":"https://mpp.api.agentmail.to"},{"url":"https://api.agentmail.eu"}],"security":[{"BearerAuth":[]},{"TokenAuth":[]}],"x-fern-sdk-group-name":["pods","domains"],"x-fern-sdk-method-name":"get"},"patch":{"description":"**CLI:**\n```bash\nagentmail pods domains update --pod-id --domain-id \n```","operationId":"pods_domains_update","tags":["PodsDomains"],"parameters":[{"name":"pod_id","in":"path","required":true,"schema":{"$ref":"#/components/schemas/podsPodId"}},{"name":"domain_id","in":"path","required":true,"schema":{"$ref":"#/components/schemas/DomainId"}}],"responses":{"200":{"description":"","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Domain"}}}},"404":{"description":"","content":{"application/json":{"schema":{"$ref":"#/components/schemas/ErrorResponse"}}}}},"summary":"Update Domain","servers":[{"url":"https://api.agentmail.to"},{"url":"https://x402.api.agentmail.to"},{"url":"https://mpp.api.agentmail.to"},{"url":"https://api.agentmail.eu"}],"security":[{"BearerAuth":[]},{"TokenAuth":[]}],"requestBody":{"required":true,"content":{"application/json":{"schema":{"$ref":"#/components/schemas/UpdateDomainRequest"}}}},"x-fern-sdk-group-name":["pods","domains"],"x-fern-sdk-method-name":"update"},"delete":{"description":"**CLI:**\n```bash\nagentmail pods domains delete --pod-id --domain-id \n```","operationId":"pods_domains_delete","tags":["PodsDomains"],"parameters":[{"name":"pod_id","in":"path","required":true,"schema":{"$ref":"#/components/schemas/podsPodId"}},{"name":"domain_id","in":"path","required":true,"schema":{"$ref":"#/components/schemas/DomainId"}}],"responses":{"204":{"description":""},"404":{"description":"","content":{"application/json":{"schema":{"$ref":"#/components/schemas/ErrorResponse"}}}}},"summary":"Delete Domain","servers":[{"url":"https://api.agentmail.to"},{"url":"https://x402.api.agentmail.to"},{"url":"https://mpp.api.agentmail.to"},{"url":"https://api.agentmail.eu"}],"security":[{"BearerAuth":[]},{"TokenAuth":[]}],"x-fern-sdk-group-name":["pods","domains"],"x-fern-sdk-method-name":"delete"}},"/v0/pods/{pod_id}/domains/{domain_id}/zone-file":{"get":{"description":"**CLI:**\n```bash\nagentmail pods domains get-zone-file --pod-id --domain-id \n```","operationId":"pods_domains_getZoneFile","tags":["PodsDomains"],"parameters":[{"name":"pod_id","in":"path","required":true,"schema":{"$ref":"#/components/schemas/podsPodId"}},{"name":"domain_id","in":"path","required":true,"schema":{"$ref":"#/components/schemas/DomainId"}}],"responses":{"204":{"description":""},"404":{"description":"","content":{"application/json":{"schema":{"$ref":"#/components/schemas/ErrorResponse"}}}}},"summary":"Get Zone File","servers":[{"url":"https://api.agentmail.to"},{"url":"https://x402.api.agentmail.to"},{"url":"https://mpp.api.agentmail.to"},{"url":"https://api.agentmail.eu"}],"security":[{"BearerAuth":[]},{"TokenAuth":[]}],"x-fern-sdk-group-name":["pods","domains"],"x-fern-sdk-method-name":"get-zone-file"}},"/v0/pods/{pod_id}/domains/{domain_id}/verify":{"post":{"description":"**CLI:**\n```bash\nagentmail pods domains verify --pod-id --domain-id \n```","operationId":"pods_domains_verify","tags":["PodsDomains"],"parameters":[{"name":"pod_id","in":"path","required":true,"schema":{"$ref":"#/components/schemas/podsPodId"}},{"name":"domain_id","in":"path","required":true,"schema":{"$ref":"#/components/schemas/DomainId"}}],"responses":{"204":{"description":""},"404":{"description":"","content":{"application/json":{"schema":{"$ref":"#/components/schemas/ErrorResponse"}}}}},"summary":"Verify Domain","servers":[{"url":"https://api.agentmail.to"},{"url":"https://x402.api.agentmail.to"},{"url":"https://mpp.api.agentmail.to"},{"url":"https://api.agentmail.eu"}],"security":[{"BearerAuth":[]},{"TokenAuth":[]}],"x-fern-sdk-group-name":["pods","domains"],"x-fern-sdk-method-name":"verify"}},"/v0/pods/{pod_id}/drafts":{"get":{"description":"**CLI:**\n```bash\nagentmail pods drafts list --pod-id \n```","operationId":"pods_drafts_list","tags":["PodsDrafts"],"parameters":[{"name":"pod_id","in":"path","required":true,"schema":{"$ref":"#/components/schemas/podsPodId"}},{"name":"limit","in":"query","required":false,"schema":{"$ref":"#/components/schemas/Limit","nullable":true}},{"name":"page_token","in":"query","required":false,"schema":{"$ref":"#/components/schemas/PageToken","nullable":true}},{"name":"labels","in":"query","required":false,"schema":{"$ref":"#/components/schemas/Labels","nullable":true}},{"name":"before","in":"query","required":false,"schema":{"$ref":"#/components/schemas/Before","nullable":true}},{"name":"after","in":"query","required":false,"schema":{"$ref":"#/components/schemas/After","nullable":true}},{"name":"ascending","in":"query","required":false,"schema":{"$ref":"#/components/schemas/Ascending","nullable":true}}],"responses":{"200":{"description":"","content":{"application/json":{"schema":{"$ref":"#/components/schemas/ListDraftsResponse"}}}},"404":{"description":"","content":{"application/json":{"schema":{"$ref":"#/components/schemas/ErrorResponse"}}}}},"summary":"List Drafts","servers":[{"url":"https://api.agentmail.to"},{"url":"https://x402.api.agentmail.to"},{"url":"https://mpp.api.agentmail.to"},{"url":"https://api.agentmail.eu"}],"security":[{"BearerAuth":[]},{"TokenAuth":[]}],"x-fern-sdk-group-name":["pods","drafts"],"x-fern-sdk-method-name":"list"}},"/v0/pods/{pod_id}/drafts/{draft_id}":{"get":{"description":"**CLI:**\n```bash\nagentmail pods drafts get --pod-id --draft-id \n```","operationId":"pods_drafts_get","tags":["PodsDrafts"],"parameters":[{"name":"pod_id","in":"path","required":true,"schema":{"$ref":"#/components/schemas/podsPodId"}},{"name":"draft_id","in":"path","required":true,"schema":{"$ref":"#/components/schemas/DraftId"}}],"responses":{"200":{"description":"","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Draft"}}}},"404":{"description":"","content":{"application/json":{"schema":{"$ref":"#/components/schemas/ErrorResponse"}}}}},"summary":"Get Draft","servers":[{"url":"https://api.agentmail.to"},{"url":"https://x402.api.agentmail.to"},{"url":"https://mpp.api.agentmail.to"},{"url":"https://api.agentmail.eu"}],"security":[{"BearerAuth":[]},{"TokenAuth":[]}],"x-fern-sdk-group-name":["pods","drafts"],"x-fern-sdk-method-name":"get"}},"/v0/pods/{pod_id}/drafts/{draft_id}/attachments/{attachment_id}":{"get":{"description":"**CLI:**\n```bash\nagentmail pods drafts get-attachment --pod-id --draft-id --attachment-id \n```","operationId":"pods_drafts_getAttachment","tags":["PodsDrafts"],"parameters":[{"name":"pod_id","in":"path","required":true,"schema":{"$ref":"#/components/schemas/podsPodId"}},{"name":"draft_id","in":"path","required":true,"schema":{"$ref":"#/components/schemas/DraftId"}},{"name":"attachment_id","in":"path","required":true,"schema":{"$ref":"#/components/schemas/AttachmentId"}}],"responses":{"200":{"description":"","content":{"application/json":{"schema":{"$ref":"#/components/schemas/AttachmentResponse"}}}},"404":{"description":"","content":{"application/json":{"schema":{"$ref":"#/components/schemas/ErrorResponse"}}}}},"summary":"Get Attachment","servers":[{"url":"https://api.agentmail.to"},{"url":"https://x402.api.agentmail.to"},{"url":"https://mpp.api.agentmail.to"},{"url":"https://api.agentmail.eu"}],"security":[{"BearerAuth":[]},{"TokenAuth":[]}],"x-fern-sdk-group-name":["pods","drafts"],"x-fern-sdk-method-name":"get-attachment"}},"/v0/pods/{pod_id}/inboxes":{"get":{"description":"**CLI:**\n```bash\nagentmail pods inboxes list --pod-id \n```","operationId":"pods_inboxes_list","tags":["PodsInboxes"],"parameters":[{"name":"pod_id","in":"path","required":true,"schema":{"$ref":"#/components/schemas/podsPodId"}},{"name":"limit","in":"query","required":false,"schema":{"$ref":"#/components/schemas/Limit","nullable":true}},{"name":"page_token","in":"query","required":false,"schema":{"$ref":"#/components/schemas/PageToken","nullable":true}},{"name":"ascending","in":"query","required":false,"schema":{"$ref":"#/components/schemas/Ascending","nullable":true}}],"responses":{"200":{"description":"","content":{"application/json":{"schema":{"$ref":"#/components/schemas/inboxesListInboxesResponse"}}}},"404":{"description":"","content":{"application/json":{"schema":{"$ref":"#/components/schemas/ErrorResponse"}}}}},"summary":"List Inboxes","servers":[{"url":"https://api.agentmail.to"},{"url":"https://x402.api.agentmail.to"},{"url":"https://mpp.api.agentmail.to"},{"url":"https://api.agentmail.eu"}],"security":[{"BearerAuth":[]},{"TokenAuth":[]}],"x-fern-sdk-group-name":["pods","inboxes"],"x-fern-sdk-method-name":"list"},"post":{"description":"**CLI:**\n```bash\nagentmail pods inboxes create --pod-id --username myagent --domain example.com\n```","operationId":"pods_inboxes_create","tags":["PodsInboxes"],"parameters":[{"name":"pod_id","in":"path","required":true,"schema":{"$ref":"#/components/schemas/podsPodId"}}],"responses":{"200":{"description":"","content":{"application/json":{"schema":{"$ref":"#/components/schemas/inboxesInbox"}}}},"400":{"description":"","content":{"application/json":{"schema":{"$ref":"#/components/schemas/ValidationErrorResponse"}}}},"422":{"description":"","content":{"application/json":{"schema":{"$ref":"#/components/schemas/ErrorResponse"}}}}},"summary":"Create Inbox","servers":[{"url":"https://api.agentmail.to"},{"url":"https://x402.api.agentmail.to"},{"url":"https://mpp.api.agentmail.to"},{"url":"https://api.agentmail.eu"}],"security":[{"BearerAuth":[]},{"TokenAuth":[]}],"requestBody":{"required":true,"content":{"application/json":{"schema":{"$ref":"#/components/schemas/inboxesCreateInboxRequest"}}}},"x-fern-sdk-group-name":["pods","inboxes"],"x-fern-sdk-method-name":"create"}},"/v0/pods/{pod_id}/inboxes/{inbox_id}":{"get":{"description":"**CLI:**\n```bash\nagentmail pods inboxes get --pod-id --inbox-id \n```","operationId":"pods_inboxes_get","tags":["PodsInboxes"],"parameters":[{"name":"pod_id","in":"path","required":true,"schema":{"$ref":"#/components/schemas/podsPodId"}},{"name":"inbox_id","in":"path","required":true,"schema":{"$ref":"#/components/schemas/inboxesInboxId"}}],"responses":{"200":{"description":"","content":{"application/json":{"schema":{"$ref":"#/components/schemas/inboxesInbox"}}}},"404":{"description":"","content":{"application/json":{"schema":{"$ref":"#/components/schemas/ErrorResponse"}}}}},"summary":"Get Inbox","servers":[{"url":"https://api.agentmail.to"},{"url":"https://x402.api.agentmail.to"},{"url":"https://mpp.api.agentmail.to"},{"url":"https://api.agentmail.eu"}],"security":[{"BearerAuth":[]},{"TokenAuth":[]}],"x-fern-sdk-group-name":["pods","inboxes"],"x-fern-sdk-method-name":"get"},"patch":{"description":"**CLI:**\n```bash\nagentmail pods inboxes update --pod-id --inbox-id \n```","operationId":"pods_inboxes_update","tags":["PodsInboxes"],"parameters":[{"name":"pod_id","in":"path","required":true,"schema":{"$ref":"#/components/schemas/podsPodId"}},{"name":"inbox_id","in":"path","required":true,"schema":{"$ref":"#/components/schemas/inboxesInboxId"}}],"responses":{"200":{"description":"","content":{"application/json":{"schema":{"$ref":"#/components/schemas/inboxesInbox"}}}},"404":{"description":"","content":{"application/json":{"schema":{"$ref":"#/components/schemas/ErrorResponse"}}}}},"summary":"Update Inbox","servers":[{"url":"https://api.agentmail.to"},{"url":"https://x402.api.agentmail.to"},{"url":"https://mpp.api.agentmail.to"},{"url":"https://api.agentmail.eu"}],"security":[{"BearerAuth":[]},{"TokenAuth":[]}],"requestBody":{"required":true,"content":{"application/json":{"schema":{"$ref":"#/components/schemas/inboxesUpdateInboxRequest"}}}},"x-fern-sdk-group-name":["pods","inboxes"],"x-fern-sdk-method-name":"update"},"delete":{"description":"**CLI:**\n```bash\nagentmail pods inboxes delete --pod-id --inbox-id \n```","operationId":"pods_inboxes_delete","tags":["PodsInboxes"],"parameters":[{"name":"pod_id","in":"path","required":true,"schema":{"$ref":"#/components/schemas/podsPodId"}},{"name":"inbox_id","in":"path","required":true,"schema":{"$ref":"#/components/schemas/inboxesInboxId"}}],"responses":{"204":{"description":""},"404":{"description":"","content":{"application/json":{"schema":{"$ref":"#/components/schemas/ErrorResponse"}}}}},"summary":"Delete Inbox","servers":[{"url":"https://api.agentmail.to"},{"url":"https://x402.api.agentmail.to"},{"url":"https://mpp.api.agentmail.to"},{"url":"https://api.agentmail.eu"}],"security":[{"BearerAuth":[]},{"TokenAuth":[]}],"x-fern-sdk-group-name":["pods","inboxes"],"x-fern-sdk-method-name":"delete"}},"/v0/pods/{pod_id}/lists/{direction}/{type}":{"get":{"description":"**CLI:**\n```bash\nagentmail pods lists list --pod-id --direction --type \n```","operationId":"pods_lists_list","tags":["PodsLists"],"parameters":[{"name":"pod_id","in":"path","required":true,"schema":{"$ref":"#/components/schemas/podsPodId"}},{"name":"direction","in":"path","required":true,"schema":{"$ref":"#/components/schemas/Direction"}},{"name":"type","in":"path","required":true,"schema":{"$ref":"#/components/schemas/ListType"}},{"name":"limit","in":"query","required":false,"schema":{"$ref":"#/components/schemas/Limit","nullable":true}},{"name":"page_token","in":"query","required":false,"schema":{"$ref":"#/components/schemas/PageToken","nullable":true}}],"responses":{"200":{"description":"","content":{"application/json":{"schema":{"$ref":"#/components/schemas/PodListListEntriesResponse"}}}}},"summary":"List Entries","servers":[{"url":"https://api.agentmail.to"},{"url":"https://x402.api.agentmail.to"},{"url":"https://mpp.api.agentmail.to"},{"url":"https://api.agentmail.eu"}],"security":[{"BearerAuth":[]},{"TokenAuth":[]}],"x-fern-sdk-group-name":["pods","lists"],"x-fern-sdk-method-name":"list"},"post":{"description":"**CLI:**\n```bash\nagentmail pods lists create --pod-id --direction --type --entry user@example.com\n```","operationId":"pods_lists_create","tags":["PodsLists"],"parameters":[{"name":"pod_id","in":"path","required":true,"schema":{"$ref":"#/components/schemas/podsPodId"}},{"name":"direction","in":"path","required":true,"schema":{"$ref":"#/components/schemas/Direction"}},{"name":"type","in":"path","required":true,"schema":{"$ref":"#/components/schemas/ListType"}}],"responses":{"200":{"description":"","content":{"application/json":{"schema":{"$ref":"#/components/schemas/PodListEntry"}}}},"400":{"description":"","content":{"application/json":{"schema":{"$ref":"#/components/schemas/ValidationErrorResponse"}}}}},"summary":"Create List Entry","servers":[{"url":"https://api.agentmail.to"},{"url":"https://x402.api.agentmail.to"},{"url":"https://mpp.api.agentmail.to"},{"url":"https://api.agentmail.eu"}],"security":[{"BearerAuth":[]},{"TokenAuth":[]}],"requestBody":{"required":true,"content":{"application/json":{"schema":{"$ref":"#/components/schemas/CreateListEntryRequest"}}}},"x-fern-sdk-group-name":["pods","lists"],"x-fern-sdk-method-name":"create"}},"/v0/pods/{pod_id}/lists/{direction}/{type}/{entry}":{"get":{"description":"**CLI:**\n```bash\nagentmail pods lists get --pod-id --direction --type --entry \n```","operationId":"pods_lists_get","tags":["PodsLists"],"parameters":[{"name":"pod_id","in":"path","required":true,"schema":{"$ref":"#/components/schemas/podsPodId"}},{"name":"direction","in":"path","required":true,"schema":{"$ref":"#/components/schemas/Direction"}},{"name":"type","in":"path","required":true,"schema":{"$ref":"#/components/schemas/ListType"}},{"name":"entry","in":"path","description":"Email address or domain.","required":true,"schema":{"type":"string"}}],"responses":{"200":{"description":"","content":{"application/json":{"schema":{"$ref":"#/components/schemas/PodListEntry"}}}},"404":{"description":"","content":{"application/json":{"schema":{"$ref":"#/components/schemas/ErrorResponse"}}}}},"summary":"Get List Entry","servers":[{"url":"https://api.agentmail.to"},{"url":"https://x402.api.agentmail.to"},{"url":"https://mpp.api.agentmail.to"},{"url":"https://api.agentmail.eu"}],"security":[{"BearerAuth":[]},{"TokenAuth":[]}],"x-fern-sdk-group-name":["pods","lists"],"x-fern-sdk-method-name":"get"},"delete":{"description":"**CLI:**\n```bash\nagentmail pods lists delete --pod-id --direction --type --entry \n```","operationId":"pods_lists_delete","tags":["PodsLists"],"parameters":[{"name":"pod_id","in":"path","required":true,"schema":{"$ref":"#/components/schemas/podsPodId"}},{"name":"direction","in":"path","required":true,"schema":{"$ref":"#/components/schemas/Direction"}},{"name":"type","in":"path","required":true,"schema":{"$ref":"#/components/schemas/ListType"}},{"name":"entry","in":"path","description":"Email address or domain.","required":true,"schema":{"type":"string"}}],"responses":{"204":{"description":""},"404":{"description":"","content":{"application/json":{"schema":{"$ref":"#/components/schemas/ErrorResponse"}}}}},"summary":"Delete List Entry","servers":[{"url":"https://api.agentmail.to"},{"url":"https://x402.api.agentmail.to"},{"url":"https://mpp.api.agentmail.to"},{"url":"https://api.agentmail.eu"}],"security":[{"BearerAuth":[]},{"TokenAuth":[]}],"x-fern-sdk-group-name":["pods","lists"],"x-fern-sdk-method-name":"delete"}},"/v0/pods/{pod_id}/metrics/events":{"get":{"description":"Counts of email events (sent, delivered, bounced, etc.) over time for\nthe pod. Defaults to the last 24 hours; `start` must be within the last\n90 days, and a future `end` is clamped to now. Omit `period` for\nindividual event counts, or set it to sum counts into buckets of that\nmany seconds.\n\n**CLI:**\n```bash\nagentmail pods metrics query-events --pod-id \n```","operationId":"pods_metrics_queryEvents","tags":["PodsMetrics"],"parameters":[{"name":"pod_id","in":"path","required":true,"schema":{"$ref":"#/components/schemas/podsPodId"}},{"name":"event_types","in":"query","required":false,"schema":{"$ref":"#/components/schemas/MetricEventTypes","nullable":true}},{"name":"start","in":"query","required":false,"schema":{"$ref":"#/components/schemas/Start","nullable":true}},{"name":"end","in":"query","required":false,"schema":{"$ref":"#/components/schemas/End","nullable":true}},{"name":"period","in":"query","required":false,"schema":{"$ref":"#/components/schemas/Period","nullable":true}},{"name":"limit","in":"query","required":false,"schema":{"$ref":"#/components/schemas/MetricLimit","nullable":true}},{"name":"descending","in":"query","required":false,"schema":{"$ref":"#/components/schemas/Descending","nullable":true}}],"responses":{"200":{"description":"","content":{"application/json":{"schema":{"$ref":"#/components/schemas/QueryMetricsResponse"}}}},"400":{"description":"","content":{"application/json":{"schema":{"$ref":"#/components/schemas/ValidationErrorResponse"}}}}},"summary":"Query Events","servers":[{"url":"https://api.agentmail.to"},{"url":"https://x402.api.agentmail.to"},{"url":"https://mpp.api.agentmail.to"},{"url":"https://api.agentmail.eu"}],"security":[{"BearerAuth":[]},{"TokenAuth":[]}],"x-fern-sdk-group-name":["pods","metrics"],"x-fern-sdk-method-name":"query-events"}},"/v0/pods/{pod_id}/metrics/usage":{"get":{"description":"Cumulative usage series for the pod. Each point is the running total of\nthe usage type at that timestamp, not the change within the bucket.\nPod-scoped queries carry every usage type except `pod_count`; requested\ntypes that don't apply to the scope are ignored. Defaults to the last\n24 hours; `start` must be within the last 90 days, and a future `end`\nis clamped to now. The range divided by `period` must not exceed 1000\nbuckets.","operationId":"pods_metrics_queryUsage","tags":["PodsMetrics"],"parameters":[{"name":"pod_id","in":"path","required":true,"schema":{"$ref":"#/components/schemas/podsPodId"}},{"name":"usage_types","in":"query","required":false,"schema":{"$ref":"#/components/schemas/UsageTypes","nullable":true}},{"name":"start","in":"query","required":false,"schema":{"$ref":"#/components/schemas/Start","nullable":true}},{"name":"end","in":"query","required":false,"schema":{"$ref":"#/components/schemas/End","nullable":true}},{"name":"period","in":"query","required":false,"schema":{"$ref":"#/components/schemas/Period","nullable":true}},{"name":"limit","in":"query","required":false,"schema":{"$ref":"#/components/schemas/MetricLimit","nullable":true}},{"name":"descending","in":"query","required":false,"schema":{"$ref":"#/components/schemas/Descending","nullable":true}}],"responses":{"200":{"description":"","content":{"application/json":{"schema":{"$ref":"#/components/schemas/QueryUsageResponse"}}}},"400":{"description":"","content":{"application/json":{"schema":{"$ref":"#/components/schemas/ValidationErrorResponse"}}}}},"summary":"Query Usage","servers":[{"url":"https://api.agentmail.to"},{"url":"https://x402.api.agentmail.to"},{"url":"https://mpp.api.agentmail.to"},{"url":"https://api.agentmail.eu"}],"security":[{"BearerAuth":[]},{"TokenAuth":[]}],"x-fern-sdk-group-name":["pods","metrics"],"x-fern-sdk-method-name":"query-usage"}},"/v0/pods/{pod_id}/threads":{"get":{"description":"Lists threads in the pod, most recent first. Pass `senders`,\n`recipients`, or `subject` to filter by substring. Filtered requests are\nserved by search, which caps `limit` at 100. For relevance-ranked\nfull-text search, use `Search Threads`.\n\n**CLI:**\n```bash\nagentmail pods threads list --pod-id \n```","operationId":"pods_threads_list","tags":["PodsThreads"],"parameters":[{"name":"pod_id","in":"path","required":true,"schema":{"$ref":"#/components/schemas/podsPodId"}},{"name":"limit","in":"query","required":false,"schema":{"$ref":"#/components/schemas/Limit","nullable":true}},{"name":"page_token","in":"query","required":false,"schema":{"$ref":"#/components/schemas/PageToken","nullable":true}},{"name":"labels","in":"query","required":false,"schema":{"$ref":"#/components/schemas/Labels","nullable":true}},{"name":"before","in":"query","required":false,"schema":{"$ref":"#/components/schemas/Before","nullable":true}},{"name":"after","in":"query","required":false,"schema":{"$ref":"#/components/schemas/After","nullable":true}},{"name":"ascending","in":"query","required":false,"schema":{"$ref":"#/components/schemas/Ascending","nullable":true}},{"name":"include_spam","in":"query","required":false,"schema":{"$ref":"#/components/schemas/IncludeSpam","nullable":true}},{"name":"include_blocked","in":"query","required":false,"schema":{"$ref":"#/components/schemas/IncludeBlocked","nullable":true}},{"name":"include_unauthenticated","in":"query","required":false,"schema":{"$ref":"#/components/schemas/IncludeUnauthenticated","nullable":true}},{"name":"include_trash","in":"query","required":false,"schema":{"$ref":"#/components/schemas/IncludeTrash","nullable":true}},{"name":"senders","in":"query","description":"Filter to threads whose senders contain this value (substring match). Repeatable; all values must match.","required":false,"schema":{"type":"array","items":{"type":"string"},"nullable":true}},{"name":"recipients","in":"query","description":"Filter to threads whose recipients contain this value (substring match). Repeatable; all values must match.","required":false,"schema":{"type":"array","items":{"type":"string"},"nullable":true}},{"name":"subject","in":"query","description":"Filter to threads whose subject contains this value (substring match). Repeatable; all values must match.","required":false,"schema":{"type":"array","items":{"type":"string"},"nullable":true}}],"responses":{"200":{"description":"","content":{"application/json":{"schema":{"$ref":"#/components/schemas/ListThreadsResponse"}}}},"404":{"description":"","content":{"application/json":{"schema":{"$ref":"#/components/schemas/ErrorResponse"}}}}},"summary":"List Threads","servers":[{"url":"https://api.agentmail.to"},{"url":"https://x402.api.agentmail.to"},{"url":"https://mpp.api.agentmail.to"},{"url":"https://api.agentmail.eu"}],"security":[{"BearerAuth":[]},{"TokenAuth":[]}],"x-fern-sdk-group-name":["pods","threads"],"x-fern-sdk-method-name":"list"}},"/v0/pods/{pod_id}/threads/search":{"get":{"description":"Full-text search across threads in the pod, ranked by relevance. The\nquery is matched against senders, recipients, and subject (substring)\nand the message body (tokenized full text). Spam, trash, blocked, and\nunauthenticated threads are always excluded. `limit` cannot exceed 100.","operationId":"pods_threads_search","tags":["PodsThreads"],"parameters":[{"name":"pod_id","in":"path","required":true,"schema":{"$ref":"#/components/schemas/podsPodId"}},{"name":"q","in":"query","required":true,"schema":{"$ref":"#/components/schemas/Query"}},{"name":"limit","in":"query","required":false,"schema":{"$ref":"#/components/schemas/Limit","nullable":true}},{"name":"page_token","in":"query","required":false,"schema":{"$ref":"#/components/schemas/PageToken","nullable":true}},{"name":"before","in":"query","required":false,"schema":{"$ref":"#/components/schemas/Before","nullable":true}},{"name":"after","in":"query","required":false,"schema":{"$ref":"#/components/schemas/After","nullable":true}}],"responses":{"200":{"description":"","content":{"application/json":{"schema":{"$ref":"#/components/schemas/SearchThreadsResponse"}}}},"400":{"description":"","content":{"application/json":{"schema":{"$ref":"#/components/schemas/ValidationErrorResponse"}}}},"404":{"description":"","content":{"application/json":{"schema":{"$ref":"#/components/schemas/ErrorResponse"}}}}},"summary":"Search Threads","servers":[{"url":"https://api.agentmail.to"},{"url":"https://x402.api.agentmail.to"},{"url":"https://mpp.api.agentmail.to"},{"url":"https://api.agentmail.eu"}],"security":[{"BearerAuth":[]},{"TokenAuth":[]}],"x-fern-sdk-group-name":["pods","threads"],"x-fern-sdk-method-name":"search"}},"/v0/pods/{pod_id}/threads/{thread_id}":{"get":{"description":"**CLI:**\n```bash\nagentmail pods threads get --pod-id --thread-id \n```","operationId":"pods_threads_get","tags":["PodsThreads"],"parameters":[{"name":"pod_id","in":"path","required":true,"schema":{"$ref":"#/components/schemas/podsPodId"}},{"name":"thread_id","in":"path","required":true,"schema":{"$ref":"#/components/schemas/ThreadId"}}],"responses":{"200":{"description":"","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Thread"}}}},"404":{"description":"","content":{"application/json":{"schema":{"$ref":"#/components/schemas/ErrorResponse"}}}}},"summary":"Get Thread","servers":[{"url":"https://api.agentmail.to"},{"url":"https://x402.api.agentmail.to"},{"url":"https://mpp.api.agentmail.to"},{"url":"https://api.agentmail.eu"}],"security":[{"BearerAuth":[]},{"TokenAuth":[]}],"x-fern-sdk-group-name":["pods","threads"],"x-fern-sdk-method-name":"get"},"patch":{"description":"Updates thread labels. Cannot add or remove system labels (sent, received, bounced, etc.). Rejects requests with a `422` for threads with 100 or more messages.","operationId":"pods_threads_update","tags":["PodsThreads"],"parameters":[{"name":"pod_id","in":"path","required":true,"schema":{"$ref":"#/components/schemas/podsPodId"}},{"name":"thread_id","in":"path","required":true,"schema":{"$ref":"#/components/schemas/ThreadId"}}],"responses":{"200":{"description":"","content":{"application/json":{"schema":{"$ref":"#/components/schemas/UpdateThreadResponse"}}}},"400":{"description":"","content":{"application/json":{"schema":{"$ref":"#/components/schemas/ValidationErrorResponse"}}}},"404":{"description":"","content":{"application/json":{"schema":{"$ref":"#/components/schemas/ErrorResponse"}}}},"422":{"description":"","content":{"application/json":{"schema":{"$ref":"#/components/schemas/ErrorResponse"}}}}},"summary":"Update Thread","servers":[{"url":"https://api.agentmail.to"},{"url":"https://x402.api.agentmail.to"},{"url":"https://mpp.api.agentmail.to"},{"url":"https://api.agentmail.eu"}],"security":[{"BearerAuth":[]},{"TokenAuth":[]}],"requestBody":{"required":true,"content":{"application/json":{"schema":{"$ref":"#/components/schemas/UpdateThreadRequest"}}}},"x-fern-sdk-group-name":["pods","threads"],"x-fern-sdk-method-name":"update"},"delete":{"description":"Permanently deletes a thread and all of its messages.\n\n**CLI:**\n```bash\nagentmail pods threads delete --pod-id --thread-id \n```","operationId":"pods_threads_delete","tags":["PodsThreads"],"parameters":[{"name":"pod_id","in":"path","required":true,"schema":{"$ref":"#/components/schemas/podsPodId"}},{"name":"thread_id","in":"path","required":true,"schema":{"$ref":"#/components/schemas/ThreadId"}}],"responses":{"204":{"description":""},"404":{"description":"","content":{"application/json":{"schema":{"$ref":"#/components/schemas/ErrorResponse"}}}}},"summary":"Delete Thread","servers":[{"url":"https://api.agentmail.to"},{"url":"https://x402.api.agentmail.to"},{"url":"https://mpp.api.agentmail.to"},{"url":"https://api.agentmail.eu"}],"security":[{"BearerAuth":[]},{"TokenAuth":[]}],"x-fern-sdk-group-name":["pods","threads"],"x-fern-sdk-method-name":"delete"}},"/v0/pods/{pod_id}/threads/{thread_id}/attachments/{attachment_id}":{"get":{"description":"**CLI:**\n```bash\nagentmail pods threads get-attachment --pod-id --thread-id --attachment-id \n```","operationId":"pods_threads_getAttachment","tags":["PodsThreads"],"parameters":[{"name":"pod_id","in":"path","required":true,"schema":{"$ref":"#/components/schemas/podsPodId"}},{"name":"thread_id","in":"path","required":true,"schema":{"$ref":"#/components/schemas/ThreadId"}},{"name":"attachment_id","in":"path","required":true,"schema":{"$ref":"#/components/schemas/AttachmentId"}}],"responses":{"200":{"description":"","content":{"application/json":{"schema":{"$ref":"#/components/schemas/AttachmentResponse"}}}},"404":{"description":"","content":{"application/json":{"schema":{"$ref":"#/components/schemas/ErrorResponse"}}}}},"summary":"Get Attachment","servers":[{"url":"https://api.agentmail.to"},{"url":"https://x402.api.agentmail.to"},{"url":"https://mpp.api.agentmail.to"},{"url":"https://api.agentmail.eu"}],"security":[{"BearerAuth":[]},{"TokenAuth":[]}],"x-fern-sdk-group-name":["pods","threads"],"x-fern-sdk-method-name":"get-attachment"}},"/v0/pods/{pod_id}/webhooks":{"get":{"description":"**CLI:**\n```bash\nagentmail pods webhooks list --pod-id \n```","operationId":"pods_webhooks_list","tags":["PodsWebhooks"],"parameters":[{"name":"pod_id","in":"path","required":true,"schema":{"$ref":"#/components/schemas/podsPodId"}},{"name":"limit","in":"query","required":false,"schema":{"$ref":"#/components/schemas/Limit","nullable":true}},{"name":"page_token","in":"query","required":false,"schema":{"$ref":"#/components/schemas/PageToken","nullable":true}},{"name":"ascending","in":"query","required":false,"schema":{"$ref":"#/components/schemas/Ascending","nullable":true}}],"responses":{"200":{"description":"","content":{"application/json":{"schema":{"$ref":"#/components/schemas/webhooksListWebhooksResponse"}}}}},"summary":"List Webhooks","servers":[{"url":"https://api.agentmail.to"},{"url":"https://x402.api.agentmail.to"},{"url":"https://mpp.api.agentmail.to"},{"url":"https://api.agentmail.eu"}],"security":[{"BearerAuth":[]},{"TokenAuth":[]}],"x-fern-sdk-group-name":["pods","webhooks"],"x-fern-sdk-method-name":"list"},"post":{"description":"Create a webhook scoped to this pod.\n\n**CLI:**\n```bash\nagentmail pods webhooks create --pod-id --url https://example.com/webhook --event-types message.received\n```","operationId":"pods_webhooks_create","tags":["PodsWebhooks"],"parameters":[{"name":"pod_id","in":"path","required":true,"schema":{"$ref":"#/components/schemas/podsPodId"}}],"responses":{"200":{"description":"","content":{"application/json":{"schema":{"$ref":"#/components/schemas/webhooksWebhook"}}}},"400":{"description":"","content":{"application/json":{"schema":{"$ref":"#/components/schemas/ValidationErrorResponse"}}}}},"summary":"Create Webhook","servers":[{"url":"https://api.agentmail.to"},{"url":"https://x402.api.agentmail.to"},{"url":"https://mpp.api.agentmail.to"},{"url":"https://api.agentmail.eu"}],"security":[{"BearerAuth":[]},{"TokenAuth":[]}],"requestBody":{"required":true,"content":{"application/json":{"schema":{"$ref":"#/components/schemas/webhooksCreatePodWebhookRequest"}}}},"x-fern-sdk-group-name":["pods","webhooks"],"x-fern-sdk-method-name":"create"}},"/v0/pods/{pod_id}/webhooks/{webhook_id}":{"get":{"description":"**CLI:**\n```bash\nagentmail pods webhooks get --pod-id --webhook-id \n```","operationId":"pods_webhooks_get","tags":["PodsWebhooks"],"parameters":[{"name":"pod_id","in":"path","required":true,"schema":{"$ref":"#/components/schemas/podsPodId"}},{"name":"webhook_id","in":"path","required":true,"schema":{"$ref":"#/components/schemas/webhooksWebhookId"}}],"responses":{"200":{"description":"","content":{"application/json":{"schema":{"$ref":"#/components/schemas/webhooksWebhook"}}}},"404":{"description":"","content":{"application/json":{"schema":{"$ref":"#/components/schemas/ErrorResponse"}}}}},"summary":"Get Webhook","servers":[{"url":"https://api.agentmail.to"},{"url":"https://x402.api.agentmail.to"},{"url":"https://mpp.api.agentmail.to"},{"url":"https://api.agentmail.eu"}],"security":[{"BearerAuth":[]},{"TokenAuth":[]}],"x-fern-sdk-group-name":["pods","webhooks"],"x-fern-sdk-method-name":"get"},"patch":{"description":"**CLI:**\n```bash\nagentmail pods webhooks update --pod-id --webhook-id --add-inbox-ids \n```","operationId":"pods_webhooks_update","tags":["PodsWebhooks"],"parameters":[{"name":"pod_id","in":"path","required":true,"schema":{"$ref":"#/components/schemas/podsPodId"}},{"name":"webhook_id","in":"path","required":true,"schema":{"$ref":"#/components/schemas/webhooksWebhookId"}}],"responses":{"200":{"description":"","content":{"application/json":{"schema":{"$ref":"#/components/schemas/webhooksWebhook"}}}},"400":{"description":"","content":{"application/json":{"schema":{"$ref":"#/components/schemas/ValidationErrorResponse"}}}},"404":{"description":"","content":{"application/json":{"schema":{"$ref":"#/components/schemas/ErrorResponse"}}}}},"summary":"Update Webhook","servers":[{"url":"https://api.agentmail.to"},{"url":"https://x402.api.agentmail.to"},{"url":"https://mpp.api.agentmail.to"},{"url":"https://api.agentmail.eu"}],"security":[{"BearerAuth":[]},{"TokenAuth":[]}],"requestBody":{"required":true,"content":{"application/json":{"schema":{"$ref":"#/components/schemas/webhooksUpdatePodWebhookRequest"}}}},"x-fern-sdk-group-name":["pods","webhooks"],"x-fern-sdk-method-name":"update"},"delete":{"description":"**CLI:**\n```bash\nagentmail pods webhooks delete --pod-id --webhook-id \n```","operationId":"pods_webhooks_delete","tags":["PodsWebhooks"],"parameters":[{"name":"pod_id","in":"path","required":true,"schema":{"$ref":"#/components/schemas/podsPodId"}},{"name":"webhook_id","in":"path","required":true,"schema":{"$ref":"#/components/schemas/webhooksWebhookId"}}],"responses":{"204":{"description":""},"404":{"description":"","content":{"application/json":{"schema":{"$ref":"#/components/schemas/ErrorResponse"}}}}},"summary":"Delete Webhook","servers":[{"url":"https://api.agentmail.to"},{"url":"https://x402.api.agentmail.to"},{"url":"https://mpp.api.agentmail.to"},{"url":"https://api.agentmail.eu"}],"security":[{"BearerAuth":[]},{"TokenAuth":[]}],"x-fern-sdk-group-name":["pods","webhooks"],"x-fern-sdk-method-name":"delete"}},"/v0/pods/{pod_id}/webhooks/{webhook_id}/headers":{"get":{"description":"List the names of custom HTTP headers included with deliveries to this pod-scoped webhook.\nHeader values are write-only and are never returned.","operationId":"pods_webhooks_getHeaders","tags":["PodsWebhooks"],"parameters":[{"name":"pod_id","in":"path","required":true,"schema":{"$ref":"#/components/schemas/podsPodId"}},{"name":"webhook_id","in":"path","required":true,"schema":{"$ref":"#/components/schemas/webhooksWebhookId"}}],"responses":{"200":{"description":"","content":{"application/json":{"schema":{"$ref":"#/components/schemas/webhooksWebhookHeaderNamesResponse"}}}},"404":{"description":"","content":{"application/json":{"schema":{"$ref":"#/components/schemas/ErrorResponse"}}}}},"summary":"Get Webhook Headers","servers":[{"url":"https://api.agentmail.to"},{"url":"https://x402.api.agentmail.to"},{"url":"https://mpp.api.agentmail.to"},{"url":"https://api.agentmail.eu"}],"security":[{"BearerAuth":[]},{"TokenAuth":[]}],"x-fern-sdk-group-name":["pods","webhooks"],"x-fern-sdk-method-name":"get-headers"},"patch":{"description":"Atomically set, replace, or remove custom HTTP headers included with deliveries to this\npod-scoped webhook. Header values remain write-only.","operationId":"pods_webhooks_updateHeaders","tags":["PodsWebhooks"],"parameters":[{"name":"pod_id","in":"path","required":true,"schema":{"$ref":"#/components/schemas/podsPodId"}},{"name":"webhook_id","in":"path","required":true,"schema":{"$ref":"#/components/schemas/webhooksWebhookId"}}],"responses":{"204":{"description":""},"400":{"description":"","content":{"application/json":{"schema":{"$ref":"#/components/schemas/ValidationErrorResponse"}}}},"404":{"description":"","content":{"application/json":{"schema":{"$ref":"#/components/schemas/ErrorResponse"}}}}},"summary":"Update Webhook Headers","servers":[{"url":"https://api.agentmail.to"},{"url":"https://x402.api.agentmail.to"},{"url":"https://mpp.api.agentmail.to"},{"url":"https://api.agentmail.eu"}],"security":[{"BearerAuth":[]},{"TokenAuth":[]}],"requestBody":{"required":true,"content":{"application/json":{"schema":{"$ref":"#/components/schemas/webhooksUpdateWebhookHeadersRequest"}}}},"x-fern-sdk-group-name":["pods","webhooks"],"x-fern-sdk-method-name":"update-headers"}},"/v0/threads":{"get":{"description":"Lists threads, most recent first. Pass `senders`, `recipients`, or\n`subject` to filter by substring. Filtered requests are served by\nsearch, which caps `limit` at 100. For relevance-ranked full-text\nsearch across senders, recipients, subject, and message body, use\n`Search Threads`.\n\n**CLI:**\n```bash\nagentmail threads list\n```","operationId":"threads_list","tags":["Threads"],"parameters":[{"name":"limit","in":"query","required":false,"schema":{"$ref":"#/components/schemas/Limit","nullable":true}},{"name":"page_token","in":"query","required":false,"schema":{"$ref":"#/components/schemas/PageToken","nullable":true}},{"name":"labels","in":"query","required":false,"schema":{"$ref":"#/components/schemas/Labels","nullable":true}},{"name":"before","in":"query","required":false,"schema":{"$ref":"#/components/schemas/Before","nullable":true}},{"name":"after","in":"query","required":false,"schema":{"$ref":"#/components/schemas/After","nullable":true}},{"name":"ascending","in":"query","required":false,"schema":{"$ref":"#/components/schemas/Ascending","nullable":true}},{"name":"include_spam","in":"query","required":false,"schema":{"$ref":"#/components/schemas/IncludeSpam","nullable":true}},{"name":"include_blocked","in":"query","required":false,"schema":{"$ref":"#/components/schemas/IncludeBlocked","nullable":true}},{"name":"include_unauthenticated","in":"query","required":false,"schema":{"$ref":"#/components/schemas/IncludeUnauthenticated","nullable":true}},{"name":"include_trash","in":"query","required":false,"schema":{"$ref":"#/components/schemas/IncludeTrash","nullable":true}},{"name":"senders","in":"query","description":"Filter to threads whose senders contain this value (substring match). Repeatable; all values must match.","required":false,"schema":{"type":"array","items":{"type":"string"},"nullable":true}},{"name":"recipients","in":"query","description":"Filter to threads whose recipients contain this value (substring match). Repeatable; all values must match.","required":false,"schema":{"type":"array","items":{"type":"string"},"nullable":true}},{"name":"subject","in":"query","description":"Filter to threads whose subject contains this value (substring match). Repeatable; all values must match.","required":false,"schema":{"type":"array","items":{"type":"string"},"nullable":true}}],"responses":{"200":{"description":"","content":{"application/json":{"schema":{"$ref":"#/components/schemas/ListThreadsResponse"}}}},"404":{"description":"","content":{"application/json":{"schema":{"$ref":"#/components/schemas/ErrorResponse"}}}}},"summary":"List Threads","servers":[{"url":"https://api.agentmail.to"},{"url":"https://x402.api.agentmail.to"},{"url":"https://mpp.api.agentmail.to"},{"url":"https://api.agentmail.eu"}],"security":[{"BearerAuth":[]},{"TokenAuth":[]}],"x-fern-sdk-group-name":["threads"],"x-fern-sdk-method-name":"list"}},"/v0/threads/search":{"get":{"description":"Full-text search across threads in the organization, ranked by\nrelevance. The query is matched against senders, recipients, and\nsubject (substring) and the message body (tokenized full text). Spam,\ntrash, blocked, and unauthenticated threads are always excluded.\n`limit` cannot exceed 100.","operationId":"threads_search","tags":["Threads"],"parameters":[{"name":"q","in":"query","required":true,"schema":{"$ref":"#/components/schemas/Query"}},{"name":"limit","in":"query","required":false,"schema":{"$ref":"#/components/schemas/Limit","nullable":true}},{"name":"page_token","in":"query","required":false,"schema":{"$ref":"#/components/schemas/PageToken","nullable":true}},{"name":"before","in":"query","required":false,"schema":{"$ref":"#/components/schemas/Before","nullable":true}},{"name":"after","in":"query","required":false,"schema":{"$ref":"#/components/schemas/After","nullable":true}}],"responses":{"200":{"description":"","content":{"application/json":{"schema":{"$ref":"#/components/schemas/SearchThreadsResponse"}}}},"400":{"description":"","content":{"application/json":{"schema":{"$ref":"#/components/schemas/ValidationErrorResponse"}}}},"404":{"description":"","content":{"application/json":{"schema":{"$ref":"#/components/schemas/ErrorResponse"}}}}},"summary":"Search Threads","servers":[{"url":"https://api.agentmail.to"},{"url":"https://x402.api.agentmail.to"},{"url":"https://mpp.api.agentmail.to"},{"url":"https://api.agentmail.eu"}],"security":[{"BearerAuth":[]},{"TokenAuth":[]}],"x-fern-sdk-group-name":["threads"],"x-fern-sdk-method-name":"search"}},"/v0/threads/{thread_id}":{"get":{"description":"**CLI:**\n```bash\nagentmail threads get --thread-id \n```","operationId":"threads_get","tags":["Threads"],"parameters":[{"name":"thread_id","in":"path","required":true,"schema":{"$ref":"#/components/schemas/ThreadId"}}],"responses":{"200":{"description":"","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Thread"}}}},"404":{"description":"","content":{"application/json":{"schema":{"$ref":"#/components/schemas/ErrorResponse"}}}}},"summary":"Get Thread","servers":[{"url":"https://api.agentmail.to"},{"url":"https://x402.api.agentmail.to"},{"url":"https://mpp.api.agentmail.to"},{"url":"https://api.agentmail.eu"}],"security":[{"BearerAuth":[]},{"TokenAuth":[]}],"x-fern-sdk-group-name":["threads"],"x-fern-sdk-method-name":"get"},"patch":{"description":"Updates thread labels. Cannot add or remove system labels (sent, received, bounced, etc.). Rejects requests with a `422` for threads with 100 or more messages.","operationId":"threads_update","tags":["Threads"],"parameters":[{"name":"thread_id","in":"path","required":true,"schema":{"$ref":"#/components/schemas/ThreadId"}}],"responses":{"200":{"description":"","content":{"application/json":{"schema":{"$ref":"#/components/schemas/UpdateThreadResponse"}}}},"400":{"description":"","content":{"application/json":{"schema":{"$ref":"#/components/schemas/ValidationErrorResponse"}}}},"404":{"description":"","content":{"application/json":{"schema":{"$ref":"#/components/schemas/ErrorResponse"}}}},"422":{"description":"","content":{"application/json":{"schema":{"$ref":"#/components/schemas/ErrorResponse"}}}}},"summary":"Update Thread","servers":[{"url":"https://api.agentmail.to"},{"url":"https://x402.api.agentmail.to"},{"url":"https://mpp.api.agentmail.to"},{"url":"https://api.agentmail.eu"}],"security":[{"BearerAuth":[]},{"TokenAuth":[]}],"requestBody":{"required":true,"content":{"application/json":{"schema":{"$ref":"#/components/schemas/UpdateThreadRequest"}}}},"x-fern-sdk-group-name":["threads"],"x-fern-sdk-method-name":"update"},"delete":{"description":"Permanently deletes a thread and all of its messages.\n\n**CLI:**\n```bash\nagentmail threads delete --thread-id \n```","operationId":"threads_delete","tags":["Threads"],"parameters":[{"name":"thread_id","in":"path","required":true,"schema":{"$ref":"#/components/schemas/ThreadId"}}],"responses":{"204":{"description":""},"404":{"description":"","content":{"application/json":{"schema":{"$ref":"#/components/schemas/ErrorResponse"}}}}},"summary":"Delete Thread","servers":[{"url":"https://api.agentmail.to"},{"url":"https://x402.api.agentmail.to"},{"url":"https://mpp.api.agentmail.to"},{"url":"https://api.agentmail.eu"}],"security":[{"BearerAuth":[]},{"TokenAuth":[]}],"x-fern-sdk-group-name":["threads"],"x-fern-sdk-method-name":"delete"}},"/v0/threads/{thread_id}/attachments/{attachment_id}":{"get":{"description":"**CLI:**\n```bash\nagentmail threads get-attachment --thread-id --attachment-id \n```","operationId":"threads_getAttachment","tags":["Threads"],"parameters":[{"name":"thread_id","in":"path","required":true,"schema":{"$ref":"#/components/schemas/ThreadId"}},{"name":"attachment_id","in":"path","required":true,"schema":{"$ref":"#/components/schemas/AttachmentId"}}],"responses":{"200":{"description":"","content":{"application/json":{"schema":{"$ref":"#/components/schemas/AttachmentResponse"}}}},"404":{"description":"","content":{"application/json":{"schema":{"$ref":"#/components/schemas/ErrorResponse"}}}}},"summary":"Get Attachment","servers":[{"url":"https://api.agentmail.to"},{"url":"https://x402.api.agentmail.to"},{"url":"https://mpp.api.agentmail.to"},{"url":"https://api.agentmail.eu"}],"security":[{"BearerAuth":[]},{"TokenAuth":[]}],"x-fern-sdk-group-name":["threads"],"x-fern-sdk-method-name":"get-attachment"}}},"components":{"schemas":{"Limit":{"title":"Limit","type":"integer","description":"Limit of number of items returned."},"Count":{"title":"Count","type":"integer","description":"Number of items returned."},"PageToken":{"title":"PageToken","type":"string","description":"Page token for pagination."},"Labels":{"title":"Labels","type":"array","items":{"type":"string"},"description":"Labels to filter by."},"Before":{"title":"Before","type":"string","format":"date-time","description":"Timestamp before which to filter by."},"After":{"title":"After","type":"string","format":"date-time","description":"Timestamp after which to filter by."},"Ascending":{"title":"Ascending","type":"boolean","description":"Sort in ascending temporal order."},"IncludeSpam":{"title":"IncludeSpam","type":"boolean","description":"Include spam in results."},"IncludeBlocked":{"title":"IncludeBlocked","type":"boolean","description":"Include blocked in results."},"IncludeUnauthenticated":{"title":"IncludeUnauthenticated","type":"boolean","description":"Include unauthenticated in results."},"IncludeTrash":{"title":"IncludeTrash","type":"boolean","description":"Include trash in results."},"OrganizationId":{"title":"OrganizationId","type":"string","description":"ID of organization."},"Query":{"title":"Query","type":"string","description":"Full-text search query. Matched against the sender, recipients, and\nsubject (substring) and the message body (tokenized full text)."},"ErrorName":{"title":"ErrorName","type":"string","description":"Name of error."},"ErrorMessage":{"title":"ErrorMessage","type":"string","description":"Error message."},"ErrorCode":{"title":"ErrorCode","type":"string","description":"Stable, machine-readable error code in snake_case (for example, not_found or missing_permission). Branch on this rather than the message text."},"ErrorFix":{"title":"ErrorFix","type":"string","description":"The concrete next action that resolves the error."},"ErrorDocs":{"title":"ErrorDocs","type":"string","description":"Link to the error reference entry for this code."},"ErrorResponse":{"title":"ErrorResponse","type":"object","properties":{"name":{"$ref":"#/components/schemas/ErrorName"},"code":{"$ref":"#/components/schemas/ErrorCode","nullable":true},"message":{"$ref":"#/components/schemas/ErrorMessage"},"fix":{"$ref":"#/components/schemas/ErrorFix","nullable":true},"docs":{"$ref":"#/components/schemas/ErrorDocs","nullable":true}},"required":["name","message"]},"ValidationErrorResponse":{"title":"ValidationErrorResponse","type":"object","properties":{"name":{"$ref":"#/components/schemas/ErrorName"},"code":{"$ref":"#/components/schemas/ErrorCode","nullable":true},"message":{"$ref":"#/components/schemas/ErrorMessage","nullable":true},"errors":{"description":"Validation errors. Each entry has a path and a message identifying the invalid field."},"fix":{"$ref":"#/components/schemas/ErrorFix","nullable":true},"docs":{"$ref":"#/components/schemas/ErrorDocs","nullable":true}},"required":["name","errors"]},"inboxesInboxId":{"title":"inboxesInboxId","type":"string","description":"The ID of the inbox."},"inboxesEmail":{"title":"inboxesEmail","type":"string","description":"Email address of the inbox."},"inboxesDisplayName":{"title":"inboxesDisplayName","type":"string","description":"Display name: `Display Name `."},"inboxesClientId":{"title":"inboxesClientId","type":"string","description":"Client ID of inbox."},"inboxesMetadataValue":{"title":"inboxesMetadataValue","oneOf":[{"type":"string"},{"type":"number","format":"double"},{"type":"boolean"}],"description":"A metadata value. May be a string, number, or boolean."},"inboxesMetadata":{"title":"inboxesMetadata","type":"object","additionalProperties":{"$ref":"#/components/schemas/inboxesMetadataValue"},"description":"Custom key-value pairs attached to the inbox. Up to 256 keys. Keys and\nstring values are each limited to 256 characters. When updating metadata,\nsend a key with a null value to remove that key."},"inboxesUpdateMetadata":{"title":"inboxesUpdateMetadata","type":"object","additionalProperties":{"$ref":"#/components/schemas/inboxesMetadataValue","nullable":true},"description":"Custom key-value pairs to merge into the inbox's existing metadata. A\nvalue may be a string, number, boolean, or null. Setting a key to null\nremoves it. Up to 256 keys; keys and string values are each limited to\n256 characters."},"inboxesInbox":{"title":"inboxesInbox","type":"object","properties":{"pod_id":{"$ref":"#/components/schemas/podsPodId"},"inbox_id":{"$ref":"#/components/schemas/inboxesInboxId"},"email":{"$ref":"#/components/schemas/inboxesEmail"},"display_name":{"$ref":"#/components/schemas/inboxesDisplayName","nullable":true},"client_id":{"$ref":"#/components/schemas/inboxesClientId","nullable":true},"metadata":{"$ref":"#/components/schemas/inboxesMetadata","nullable":true,"description":"Custom metadata attached to the inbox."},"updated_at":{"type":"string","format":"date-time","description":"Time at which inbox was last updated."},"created_at":{"type":"string","format":"date-time","description":"Time at which inbox was created."}},"required":["pod_id","inbox_id","email","updated_at","created_at"]},"inboxesListInboxesResponse":{"title":"inboxesListInboxesResponse","type":"object","properties":{"count":{"$ref":"#/components/schemas/Count"},"limit":{"$ref":"#/components/schemas/Limit","nullable":true},"next_page_token":{"$ref":"#/components/schemas/PageToken","nullable":true},"inboxes":{"type":"array","items":{"$ref":"#/components/schemas/inboxesInbox"},"description":"Ordered by `created_at` descending."}},"required":["count","inboxes"]},"inboxesCreateInboxRequest":{"title":"inboxesCreateInboxRequest","type":"object","properties":{"username":{"type":"string","nullable":true,"description":"Username of address. Randomly generated if not specified."},"domain":{"type":"string","nullable":true,"description":"Domain of address. Must be a verified domain, or any subdomain of a\nverified domain that has subdomains enabled (e.g., `bot.example.com`).\nDefaults to `agentmail.to`."},"display_name":{"$ref":"#/components/schemas/inboxesDisplayName","nullable":true},"client_id":{"$ref":"#/components/schemas/inboxesClientId","nullable":true},"metadata":{"$ref":"#/components/schemas/inboxesMetadata","nullable":true,"description":"Custom metadata to attach to the inbox."}}},"inboxesUpdateInboxRequest":{"title":"inboxesUpdateInboxRequest","type":"object","properties":{"display_name":{"$ref":"#/components/schemas/inboxesDisplayName","nullable":true},"metadata":{"$ref":"#/components/schemas/inboxesUpdateMetadata","nullable":true,"description":"Metadata to merge into the inbox's existing metadata. Keys you include\nare added or overwritten; keys you omit are left unchanged. To remove a\nsingle key, send it with a null value. To clear all metadata, send\n`metadata` as null. Sending an empty object is rejected; use null to\nclear. Each update must include at least one of `display_name` or\n`metadata`."}}},"podsPodId":{"title":"podsPodId","type":"string","description":"ID of pod."},"podsName":{"title":"podsName","type":"string","description":"Name of pod."},"podsClientId":{"title":"podsClientId","type":"string","description":"Client ID of pod."},"podsPod":{"title":"podsPod","type":"object","properties":{"pod_id":{"$ref":"#/components/schemas/podsPodId"},"name":{"$ref":"#/components/schemas/podsName"},"updated_at":{"type":"string","format":"date-time","description":"Time at which pod was last updated."},"created_at":{"type":"string","format":"date-time","description":"Time at which pod was created."},"client_id":{"$ref":"#/components/schemas/podsClientId","nullable":true}},"required":["pod_id","name","updated_at","created_at"]},"podsListPodsResponse":{"title":"podsListPodsResponse","type":"object","properties":{"count":{"$ref":"#/components/schemas/Count"},"limit":{"$ref":"#/components/schemas/Limit","nullable":true},"next_page_token":{"$ref":"#/components/schemas/PageToken","nullable":true},"pods":{"type":"array","items":{"$ref":"#/components/schemas/podsPod"},"description":"Ordered by `created_at` descending."}},"required":["count","pods"]},"podsCreatePodRequest":{"title":"podsCreatePodRequest","type":"object","properties":{"name":{"$ref":"#/components/schemas/podsName","nullable":true},"client_id":{"$ref":"#/components/schemas/podsClientId","nullable":true}}},"webhooksWebhookId":{"title":"webhooksWebhookId","type":"string","description":"ID of webhook."},"webhooksClientId":{"title":"webhooksClientId","type":"string","description":"Client ID of webhook."},"webhooksUrl":{"title":"webhooksUrl","type":"string","description":"URL of webhook endpoint."},"webhooksWebhookHeaders":{"title":"webhooksWebhookHeaders","type":"object","additionalProperties":{"type":"string"},"description":"Custom HTTP headers to include with every delivery to this webhook. Header values are write-only:\nAgentMail never returns them from webhook read endpoints. The map must contain at least one entry\nwhen provided, and every name and value must be a valid HTTP header."},"webhooksWebhookHeaderNamesResponse":{"title":"webhooksWebhookHeaderNamesResponse","type":"object","properties":{"header_names":{"type":"array","items":{"type":"string"},"description":"Names of the custom delivery headers configured for this webhook. Header values are never returned."}},"required":["header_names"]},"webhooksWebhook":{"title":"webhooksWebhook","type":"object","properties":{"webhook_id":{"$ref":"#/components/schemas/webhooksWebhookId"},"url":{"$ref":"#/components/schemas/webhooksUrl"},"event_types":{"$ref":"#/components/schemas/EventTypes","nullable":true},"pod_ids":{"$ref":"#/components/schemas/PodIds","nullable":true},"inbox_ids":{"$ref":"#/components/schemas/InboxIds","nullable":true},"secret":{"type":"string","description":"Secret for webhook signature verification."},"enabled":{"type":"boolean","description":"Webhook is enabled."},"updated_at":{"type":"string","format":"date-time","description":"Time at which webhook was last updated."},"created_at":{"type":"string","format":"date-time","description":"Time at which webhook was created."},"client_id":{"$ref":"#/components/schemas/webhooksClientId","nullable":true}},"required":["webhook_id","url","secret","enabled","updated_at","created_at"]},"webhooksListWebhooksResponse":{"title":"webhooksListWebhooksResponse","type":"object","properties":{"count":{"$ref":"#/components/schemas/Count"},"limit":{"$ref":"#/components/schemas/Limit","nullable":true},"next_page_token":{"$ref":"#/components/schemas/PageToken","nullable":true},"webhooks":{"type":"array","items":{"$ref":"#/components/schemas/webhooksWebhook"},"description":"Ordered by `created_at` descending."}},"required":["count","webhooks"]},"webhooksCreateWebhookEventTypes":{"title":"webhooksCreateWebhookEventTypes","$ref":"#/components/schemas/EventTypes","description":"Full list of event types this webhook should receive. At least one type is required. Send every type you\nwant in this array (not incremental). See [Webhooks overview](https://docs.agentmail.to/webhooks-overview)\nfor spam, blocked, and unauthenticated events and required permissions."},"webhooksUpdateWebhookEventTypes":{"title":"webhooksUpdateWebhookEventTypes","$ref":"#/components/schemas/EventTypes","description":"When you send a non-empty list, it replaces the webhook's subscribed event types in full (the same\n\"set the list\" behavior as create). It is not a merge or diff: include every event type you want after\nthe update. Sending a one-element array means the webhook will only receive that one type afterward.\nOmit this field or send an empty array to leave event types unchanged. Clearing all types with an empty\nlist is not supported. Subscribing to `message.received.spam`, `message.received.blocked`, or\n`message.received.unauthenticated` requires the matching label permission on the API key."},"webhooksCreateInboxWebhookRequest":{"title":"webhooksCreateInboxWebhookRequest","type":"object","description":"Create a webhook scoped to an inbox. The inbox comes from the path, so `inbox_ids` and `pod_ids`\nare not accepted.","properties":{"url":{"$ref":"#/components/schemas/webhooksUrl"},"event_types":{"$ref":"#/components/schemas/webhooksCreateWebhookEventTypes"},"client_id":{"$ref":"#/components/schemas/webhooksClientId","nullable":true},"headers":{"$ref":"#/components/schemas/webhooksWebhookHeaders","nullable":true}},"required":["url","event_types"]},"webhooksCreatePodWebhookRequest":{"title":"webhooksCreatePodWebhookRequest","type":"object","description":"Create a webhook scoped to a pod. The pod comes from the path, so `pod_ids` is not accepted.\nOptionally pass `inbox_ids` to narrow the webhook to specific inboxes within the pod; omit to\nreceive events for the whole pod.","properties":{"inbox_ids":{"$ref":"#/components/schemas/InboxIds","nullable":true}},"allOf":[{"$ref":"#/components/schemas/webhooksCreateInboxWebhookRequest"}]},"webhooksCreateWebhookRequest":{"title":"webhooksCreateWebhookRequest","type":"object","properties":{"pod_ids":{"$ref":"#/components/schemas/PodIds","nullable":true}},"allOf":[{"$ref":"#/components/schemas/webhooksCreatePodWebhookRequest"}]},"webhooksUpdateInboxWebhookRequest":{"title":"webhooksUpdateInboxWebhookRequest","type":"object","description":"Update an inbox-scoped webhook. It is fixed to its inbox, so only `event_types` can change.","properties":{"event_types":{"$ref":"#/components/schemas/webhooksUpdateWebhookEventTypes","nullable":true}}},"webhooksUpdatePodWebhookRequest":{"title":"webhooksUpdatePodWebhookRequest","type":"object","description":"Update a pod-scoped webhook. You can adjust which inboxes within the pod it listens to and replace\nits `event_types`, but not the pod scope itself.","properties":{"add_inbox_ids":{"$ref":"#/components/schemas/InboxIds","nullable":true,"description":"Inbox IDs to subscribe to the webhook."},"remove_inbox_ids":{"$ref":"#/components/schemas/InboxIds","nullable":true,"description":"Inbox IDs to unsubscribe from the webhook."}},"allOf":[{"$ref":"#/components/schemas/webhooksUpdateInboxWebhookRequest"}]},"webhooksUpdateWebhookRequest":{"title":"webhooksUpdateWebhookRequest","type":"object","properties":{"add_pod_ids":{"$ref":"#/components/schemas/PodIds","nullable":true,"description":"Pod IDs to subscribe to the webhook."},"remove_pod_ids":{"$ref":"#/components/schemas/PodIds","nullable":true,"description":"Pod IDs to unsubscribe from the webhook."}},"allOf":[{"$ref":"#/components/schemas/webhooksUpdatePodWebhookRequest"}]},"webhooksUpdateWebhookHeadersRequest":{"title":"webhooksUpdateWebhookHeadersRequest","type":"object","description":"Set, replace, or remove custom delivery headers. Provide at least one of `headers` or\n`remove_headers`. A header cannot be set and removed in the same request, regardless of casing.","properties":{"headers":{"$ref":"#/components/schemas/webhooksWebhookHeaders","nullable":true},"remove_headers":{"type":"array","items":{"type":"string"},"nullable":true,"description":"Names of custom delivery headers to remove."}}},"AgentSignupRequest":{"title":"AgentSignupRequest","type":"object","description":"Request body to sign up an agent.","properties":{"human_email":{"type":"string","description":"Email address of the human who owns the agent. A 6-digit OTP will be sent to this address."},"username":{"type":"string","description":"Username for the auto-created inbox (e.g. \"my-agent\" creates my-agent@agentmail.to)."},"source":{"type":"string","nullable":true,"description":"The SDK, framework, or platform issuing this sign-up (e.g. `agentmail-python`, `agentmail-cli`, `agentmail-mcp`).\nIdentifies the caller — answers \"who is signing up\".\nMax 2048 characters."},"referrer":{"type":"string","nullable":true,"description":"The channel that drove this sign-up — where the agent or its developer discovered AgentMail\n(e.g. `agent.email`, a partner URL, a campaign tag). Answers \"where did this sign-up come from\".\nMax 2048 characters."}},"required":["human_email","username"]},"AgentSignupResponse":{"title":"AgentSignupResponse","type":"object","description":"Response after successful agent sign-up.","properties":{"organization_id":{"type":"string","description":"ID of the created organization."},"inbox_id":{"type":"string","description":"ID of the auto-created inbox."},"api_key":{"type":"string","description":"API key for authenticating subsequent requests. Store this securely, it cannot be retrieved again."}},"required":["organization_id","inbox_id","api_key"]},"AgentVerifyRequest":{"title":"AgentVerifyRequest","type":"object","description":"Request body to verify an agent with an OTP code.","properties":{"otp_code":{"type":"string","description":"6-digit verification code sent to the human's email address."}},"required":["otp_code"]},"AgentVerifyResponse":{"title":"AgentVerifyResponse","type":"object","description":"Response after successful agent verification.","properties":{"verified":{"type":"boolean","description":"Whether the organization was verified."}},"required":["verified"]},"ApiKeyId":{"title":"ApiKeyId","type":"string","description":"ID of api key."},"Prefix":{"title":"Prefix","type":"string","description":"Prefix of api key."},"Name":{"title":"Name","type":"string","description":"Name of api key."},"CreatedAt":{"title":"CreatedAt","type":"string","format":"date-time","description":"Time at which api key was created."},"PublicJwkCoordinate":{"title":"PublicJwkCoordinate","type":"string","pattern":"^[A-Za-z0-9_-]{43}$","minLength":43,"maxLength":43,"description":"A 32-byte P-256 coordinate encoded as unpadded base64url."},"PublicJwk":{"title":"PublicJwk","type":"object","description":"A public P-256 JWK. The object accepts exactly `kty`, `crv`, `x`, and `y`.\nPrivate key material such as `d`, embedded key IDs, and all other members\nare rejected. The server also rejects coordinates that are not a point on\nP-256.","properties":{"kty":{"type":"string","const":"EC"},"crv":{"type":"string","const":"P-256"},"x":{"$ref":"#/components/schemas/PublicJwkCoordinate"},"y":{"$ref":"#/components/schemas/PublicJwkCoordinate"}},"required":["kty","crv","x","y"]},"OrganizationPublicKeyScope":{"title":"OrganizationPublicKeyScope","type":"object","description":"Organization-wide authority.","properties":{}},"PodPublicKeyScope":{"title":"PodPublicKeyScope","type":"object","description":"Authority over one live pod and its inboxes.","properties":{"id":{"type":"string","format":"uuid","description":"ID of the pod."}},"required":["id"]},"InboxPublicKeyScope":{"title":"InboxPublicKeyScope","type":"object","description":"Authority over one live inbox incarnation.","properties":{"id":{"type":"string","format":"email","maxLength":254,"description":"ID of the inbox."}},"required":["id"]},"PublicKeyScope":{"title":"PublicKeyScope","oneOf":[{"type":"object","allOf":[{"type":"object","properties":{"type":{"type":"string","enum":["organization"]}}},{"$ref":"#/components/schemas/OrganizationPublicKeyScope"}],"required":["type"]},{"type":"object","allOf":[{"type":"object","properties":{"type":{"type":"string","enum":["pod"]}}},{"$ref":"#/components/schemas/PodPublicKeyScope"}],"required":["type"]},{"type":"object","allOf":[{"type":"object","properties":{"type":{"type":"string","enum":["inbox"]}}},{"$ref":"#/components/schemas/InboxPublicKeyScope"}],"required":["type"]}],"description":"The immutable scope in which a public-key credential can approve AgentID sign-in."},"PublicKeyMaterial":{"title":"PublicKeyMaterial","type":"object","description":"Registered public key material and its server-computed RFC 7638 thumbprint.","properties":{"jwk":{"$ref":"#/components/schemas/PublicJwk"},"fingerprint":{"type":"string","pattern":"^[A-Za-z0-9_-]{43}$","minLength":43,"maxLength":43,"description":"RFC 7638 SHA-256 JWK thumbprint encoded as unpadded base64url."}},"required":["jwk","fingerprint"]},"PublicKeyCredential":{"title":"PublicKeyCredential","type":"object","description":"An AgentID sign-in credential. `type` and `api_key_id` are server-owned;\nuse `api_key_id` as the JWS `kid`. This response never contains a bearer\nsecret or private key.","properties":{"api_key_id":{"type":"string","format":"uuid","description":"Server-generated credential ID. Store this value as the signing key's `kid`."},"type":{"type":"string","const":"public_key","description":"Server-owned credential discriminator. Callers cannot select or update it."},"name":{"$ref":"#/components/schemas/Name","description":"Human-readable credential name."},"public_key":{"$ref":"#/components/schemas/PublicKeyMaterial"},"scope":{"$ref":"#/components/schemas/PublicKeyScope"},"expires_at":{"type":"string","format":"date-time","nullable":true,"description":"Immutable absolute expiry. Omitted when the credential does not expire."},"revoked_at":{"type":"string","format":"date-time","nullable":true,"description":"Present when organization-wide revoke-all invalidated this credential generation."},"created_at":{"type":"string","format":"date-time"},"updated_at":{"type":"string","format":"date-time"}},"required":["api_key_id","type","name","public_key","scope","created_at","updated_at"]},"CreatePublicKeyRequest":{"title":"CreatePublicKeyRequest","type":"object","description":"Register only a public P-256 JWK. Credential type, `api_key_id`, sign-in\neligibility, permissions, and generation are server-owned and are not\nrequest properties.","properties":{"public_key":{"$ref":"#/components/schemas/PublicJwk"},"name":{"type":"string","minLength":1,"maxLength":256,"nullable":true,"description":"Defaults to `AgentID key {first eight fingerprint characters}`."},"scope":{"$ref":"#/components/schemas/PublicKeyScope","nullable":true,"description":"Omit to inherit the registering bearer key's exact scope. An explicit\nscope must be the caller's scope or a live descendant."},"expires_at":{"type":"string","format":"date-time","nullable":true,"description":"Future absolute expiry. Omit to inherit the registering bearer key's\nexpiry. A child credential cannot outlive its creator."}},"required":["public_key"]},"UpdatePublicKeyNameRequest":{"title":"UpdatePublicKeyNameRequest","type":"object","description":"Rename a public-key credential. Key material, ID, type, scope, sign-in\neligibility, permissions, generation, and expiry are immutable.","properties":{"name":{"type":"string","minLength":1,"maxLength":256}},"required":["name"]},"ListPublicKeysResponse":{"title":"ListPublicKeysResponse","type":"object","properties":{"count":{"$ref":"#/components/schemas/Count"},"next_page_token":{"$ref":"#/components/schemas/PageToken","nullable":true},"public_keys":{"type":"array","items":{"$ref":"#/components/schemas/PublicKeyCredential"},"description":"Public-key credentials only, ordered by creation time descending by default."}},"required":["count","public_keys"]},"RevokeAllAgentIdSignInKeysResponse":{"title":"RevokeAllAgentIdSignInKeysResponse","type":"object","description":"Permanent idempotency receipt for an organization-wide AgentID sign-in key revocation.","properties":{"previous_generation":{"type":"integer","minimum":0},"current_generation":{"type":"integer","minimum":1},"revoked_at":{"type":"string","format":"date-time"}},"required":["previous_generation","current_generation","revoked_at"]},"BrowserEnrollmentTransactionJti":{"title":"BrowserEnrollmentTransactionJti","type":"string","pattern":"^[A-Za-z0-9_-]{22}$","minLength":22,"maxLength":22,"description":"Transaction identifier read from an enrollment action on exactly `https://auth.agentid.com`."},"CreateBrowserEnrollmentRequest":{"title":"CreateBrowserEnrollmentRequest","type":"object","description":"Attach a browser enrollment intent to one pending AgentID authorization\ntransaction. The inbox is selected by the trusted path parameter, not an\nAgentID `login_hint`.","properties":{"transaction_jti":{"$ref":"#/components/schemas/BrowserEnrollmentTransactionJti"}},"required":["transaction_jti"]},"BrowserEnrollmentAccepted":{"title":"BrowserEnrollmentAccepted","type":"object","description":"Pending enrollment receipt. The browser completes key creation and proof\non the existing AgentID page. This response contains no URL, token, or\nnavigation instruction.","properties":{"status":{"type":"string","const":"pending"},"enrollment_id":{"type":"string","format":"uuid"},"expires_at":{"type":"integer","minimum":1,"description":"Unix timestamp after which the pending enrollment cannot be activated."}},"required":["status","enrollment_id","expires_at"]},"BrowserCredentialCreator":{"title":"BrowserCredentialCreator","type":"object","properties":{"kind":{"type":"string","const":"bearer_api_key"},"api_key_id":{"type":"string","format":"uuid","description":"Bearer API key that authorized creation of the browser credential."},"created_at":{"type":"string","format":"date-time","description":"Incarnation timestamp of the authorizing bearer API key."}},"required":["kind","api_key_id","created_at"]},"BrowserCredential":{"title":"BrowserCredential","type":"object","description":"Owner-facing metadata for an active browser credential. Private key material never leaves the browser.","properties":{"credential_id":{"type":"string","format":"uuid"},"public_key_fingerprint_prefix":{"type":"string","pattern":"^[A-Za-z0-9_-]{8}$","minLength":8,"maxLength":8},"organization_id":{"type":"string","format":"uuid"},"pod_id":{"type":"string","format":"uuid"},"inbox_id":{"type":"string","format":"email","maxLength":254},"created_by":{"$ref":"#/components/schemas/BrowserCredentialCreator"},"created_at":{"type":"string","format":"date-time"},"expires_at":{"type":"string","format":"date-time"}},"required":["credential_id","public_key_fingerprint_prefix","organization_id","pod_id","inbox_id","created_by","created_at","expires_at"]},"BrowserConsent":{"title":"BrowserConsent","type":"object","description":"Remembered approval for one closed AgentID client and inbox.","properties":{"consent_id":{"type":"string","pattern":"^brc1_[A-Za-z0-9_-]{43}$","minLength":48,"maxLength":48},"inbox_id":{"type":"string","format":"email","maxLength":254},"client_type":{"type":"string","const":"closed"},"client_id":{"type":"string","minLength":1,"maxLength":2048},"client_url":{"type":"string","nullable":true,"description":"Registered client URL, when one is available."},"approved_scopes":{"type":"array","items":{"type":"string"},"description":"At least one non-empty scope approved for this client."},"created_at":{"type":"string","format":"date-time"},"updated_at":{"type":"string","format":"date-time"},"expires_at":{"type":"string","format":"date-time"}},"required":["consent_id","inbox_id","client_type","client_id","approved_scopes","created_at","updated_at","expires_at"]},"BrowserLifecycleApiKeyActor":{"title":"BrowserLifecycleApiKeyActor","type":"object","properties":{"api_key_id":{"type":"string","format":"uuid"}},"required":["api_key_id"]},"BrowserLifecycleCredentialActor":{"title":"BrowserLifecycleCredentialActor","type":"object","properties":{"credential_id":{"type":"string","format":"uuid"},"authorizing_api_key_id":{"type":"string","format":"uuid"}},"required":["credential_id","authorizing_api_key_id"]},"BrowserLifecycleActor":{"title":"BrowserLifecycleActor","oneOf":[{"type":"object","allOf":[{"type":"object","properties":{"type":{"type":"string","enum":["api_key"]}}},{"$ref":"#/components/schemas/BrowserLifecycleApiKeyActor"}],"required":["type"]},{"type":"object","allOf":[{"type":"object","properties":{"type":{"type":"string","enum":["browser_credential"]}}},{"$ref":"#/components/schemas/BrowserLifecycleCredentialActor"}],"required":["type"]}]},"BrowserEnrollmentLifecycleEventType":{"title":"BrowserEnrollmentLifecycleEventType","type":"string","enum":["browser_enrollment_intent_created","browser_credential_activated","browser_enrollment_cancelled","browser_credential_deleted"]},"BrowserConsentLifecycleEventType":{"title":"BrowserConsentLifecycleEventType","type":"string","enum":["browser_consent_created","browser_consent_updated","browser_consent_reused","browser_consent_revoked"]},"BrowserEnrollmentLifecycleEvent":{"title":"BrowserEnrollmentLifecycleEvent","type":"object","properties":{"type":{"$ref":"#/components/schemas/BrowserEnrollmentLifecycleEventType"},"trace_id":{"type":"string","format":"uuid"},"event_id":{"type":"string","format":"uuid"},"occurred_at":{"type":"string","format":"date-time"},"organization_id":{"type":"string","format":"uuid"},"pod_id":{"type":"string","format":"uuid"},"actor":{"$ref":"#/components/schemas/BrowserLifecycleActor"},"enrollment_id":{"type":"string","format":"uuid"},"credential_id":{"type":"string","format":"uuid"}},"required":["type","trace_id","event_id","occurred_at","organization_id","pod_id","actor","enrollment_id","credential_id"]},"BrowserConsentLifecycleEvent":{"title":"BrowserConsentLifecycleEvent","type":"object","properties":{"type":{"$ref":"#/components/schemas/BrowserConsentLifecycleEventType"},"trace_id":{"type":"string","format":"uuid"},"event_id":{"type":"string","format":"uuid"},"occurred_at":{"type":"string","format":"date-time"},"organization_id":{"type":"string","format":"uuid"},"pod_id":{"type":"string","format":"uuid"},"actor":{"$ref":"#/components/schemas/BrowserLifecycleActor"},"consent_id":{"type":"string","pattern":"^brc1_[A-Za-z0-9_-]{43}$","minLength":48,"maxLength":48},"client_type":{"type":"string","const":"closed"},"client_id":{"type":"string","minLength":1,"maxLength":2048}},"required":["type","trace_id","event_id","occurred_at","organization_id","pod_id","actor","consent_id","client_type","client_id"]},"BrowserLifecycleEvent":{"title":"BrowserLifecycleEvent","oneOf":[{"$ref":"#/components/schemas/BrowserEnrollmentLifecycleEvent"},{"$ref":"#/components/schemas/BrowserConsentLifecycleEvent"}]},"BrowserAuthorizationListLimit":{"title":"BrowserAuthorizationListLimit","type":"integer","minimum":1,"maximum":100},"ListBrowserCredentialsResponse":{"title":"ListBrowserCredentialsResponse","type":"object","properties":{"count":{"$ref":"#/components/schemas/Count"},"limit":{"$ref":"#/components/schemas/BrowserAuthorizationListLimit"},"next_page_token":{"$ref":"#/components/schemas/PageToken","nullable":true},"credentials":{"type":"array","items":{"$ref":"#/components/schemas/BrowserCredential"}}},"required":["count","limit","credentials"]},"ListBrowserConsentsResponse":{"title":"ListBrowserConsentsResponse","type":"object","properties":{"count":{"$ref":"#/components/schemas/Count"},"limit":{"$ref":"#/components/schemas/BrowserAuthorizationListLimit"},"next_page_token":{"$ref":"#/components/schemas/PageToken","nullable":true},"consents":{"type":"array","items":{"$ref":"#/components/schemas/BrowserConsent"}}},"required":["count","limit","consents"]},"ListBrowserLifecycleEventsResponse":{"title":"ListBrowserLifecycleEventsResponse","type":"object","properties":{"count":{"$ref":"#/components/schemas/Count"},"limit":{"$ref":"#/components/schemas/BrowserAuthorizationListLimit"},"next_page_token":{"$ref":"#/components/schemas/PageToken","nullable":true},"events":{"type":"array","items":{"$ref":"#/components/schemas/BrowserLifecycleEvent"}}},"required":["count","limit","events"]},"ApiKeyPermissions":{"title":"ApiKeyPermissions","type":"object","description":"Granular permissions for the API key. When ommitted all permissions are granted. Otherwise, only permissions set to true are granted.","properties":{"inbox_read":{"type":"boolean","nullable":true,"description":"Read inbox details."},"inbox_create":{"type":"boolean","nullable":true,"description":"Create new inboxes."},"inbox_update":{"type":"boolean","nullable":true,"description":"Update inbox settings."},"inbox_delete":{"type":"boolean","nullable":true,"description":"Delete inboxes."},"message_read":{"type":"boolean","nullable":true,"description":"Read messages. Also required to read threads."},"message_send":{"type":"boolean","nullable":true,"description":"Send messages."},"message_update":{"type":"boolean","nullable":true,"description":"Update message labels. Also required to update threads."},"message_delete":{"type":"boolean","nullable":true,"description":"Delete messages. Also required to delete threads."},"label_spam_read":{"type":"boolean","nullable":true,"description":"Access messages labeled spam."},"label_blocked_read":{"type":"boolean","nullable":true,"description":"Access messages labeled blocked."},"label_unauthenticated_read":{"type":"boolean","nullable":true,"description":"Access messages labeled unauthenticated."},"label_trash_read":{"type":"boolean","nullable":true,"description":"Access messages labeled trash."},"draft_read":{"type":"boolean","nullable":true,"description":"Read drafts."},"draft_create":{"type":"boolean","nullable":true,"description":"Create drafts."},"draft_update":{"type":"boolean","nullable":true,"description":"Update drafts."},"draft_delete":{"type":"boolean","nullable":true,"description":"Delete drafts."},"draft_send":{"type":"boolean","nullable":true,"description":"Send drafts."},"webhook_read":{"type":"boolean","nullable":true,"description":"Read webhook configurations."},"webhook_create":{"type":"boolean","nullable":true,"description":"Create webhooks."},"webhook_update":{"type":"boolean","nullable":true,"description":"Update webhooks."},"webhook_delete":{"type":"boolean","nullable":true,"description":"Delete webhooks."},"domain_read":{"type":"boolean","nullable":true,"description":"Read domain details."},"domain_create":{"type":"boolean","nullable":true,"description":"Create domains."},"domain_update":{"type":"boolean","nullable":true,"description":"Update domains."},"domain_delete":{"type":"boolean","nullable":true,"description":"Delete domains."},"list_entry_read":{"type":"boolean","nullable":true,"description":"Read list entries."},"list_entry_create":{"type":"boolean","nullable":true,"description":"Create list entries."},"list_entry_delete":{"type":"boolean","nullable":true,"description":"Delete list entries."},"metrics_read":{"type":"boolean","nullable":true,"description":"Read metrics."},"api_key_read":{"type":"boolean","nullable":true,"description":"Read API keys."},"api_key_create":{"type":"boolean","nullable":true,"description":"Create API keys."},"api_key_update":{"type":"boolean","nullable":true,"description":"Update API keys."},"api_key_delete":{"type":"boolean","nullable":true,"description":"Delete API keys."},"pod_read":{"type":"boolean","nullable":true,"description":"Read pods."},"pod_create":{"type":"boolean","nullable":true,"description":"Create pods."},"pod_delete":{"type":"boolean","nullable":true,"description":"Delete pods."}}},"ApiKey":{"title":"ApiKey","type":"object","properties":{"api_key_id":{"$ref":"#/components/schemas/ApiKeyId"},"prefix":{"$ref":"#/components/schemas/Prefix"},"name":{"$ref":"#/components/schemas/Name"},"pod_id":{"type":"string","nullable":true,"description":"Pod ID the api key is scoped to. If set, the key can only access resources within this pod."},"inbox_id":{"type":"string","nullable":true,"description":"Inbox ID the api key is scoped to. If set, the key can only access resources within this inbox."},"used_at":{"type":"string","format":"date-time","nullable":true,"description":"Time at which api key was last used."},"permissions":{"$ref":"#/components/schemas/ApiKeyPermissions","nullable":true},"created_at":{"$ref":"#/components/schemas/CreatedAt"}},"required":["api_key_id","prefix","name","created_at"]},"CreateApiKeyResponse":{"title":"CreateApiKeyResponse","type":"object","properties":{"api_key_id":{"$ref":"#/components/schemas/ApiKeyId"},"api_key":{"type":"string","description":"API key."},"prefix":{"$ref":"#/components/schemas/Prefix"},"name":{"$ref":"#/components/schemas/Name"},"pod_id":{"type":"string","nullable":true,"description":"Pod ID the api key is scoped to."},"inbox_id":{"type":"string","nullable":true,"description":"Inbox ID the api key is scoped to."},"permissions":{"$ref":"#/components/schemas/ApiKeyPermissions","nullable":true},"created_at":{"$ref":"#/components/schemas/CreatedAt"}},"required":["api_key_id","api_key","prefix","name","created_at"]},"ListApiKeysResponse":{"title":"ListApiKeysResponse","type":"object","properties":{"count":{"$ref":"#/components/schemas/Count"},"next_page_token":{"$ref":"#/components/schemas/PageToken","nullable":true},"api_keys":{"type":"array","items":{"$ref":"#/components/schemas/ApiKey"},"description":"Ordered by `created_at` descending."}},"required":["count","api_keys"]},"CreateApiKeyRequest":{"title":"CreateApiKeyRequest","type":"object","properties":{"name":{"$ref":"#/components/schemas/Name","nullable":true},"permissions":{"$ref":"#/components/schemas/ApiKeyPermissions","nullable":true}}},"AttachmentId":{"title":"AttachmentId","type":"string","description":"ID of attachment."},"AttachmentFilename":{"title":"AttachmentFilename","type":"string","description":"Filename of attachment."},"AttachmentSize":{"title":"AttachmentSize","type":"integer","description":"Size of attachment in bytes."},"AttachmentContentType":{"title":"AttachmentContentType","type":"string","description":"Content type of attachment."},"AttachmentContentDisposition":{"title":"AttachmentContentDisposition","type":"string","enum":["inline","attachment"],"description":"Content disposition of attachment."},"AttachmentContentId":{"title":"AttachmentContentId","type":"string","description":"Content ID of attachment."},"Attachment":{"title":"Attachment","type":"object","properties":{"attachment_id":{"$ref":"#/components/schemas/AttachmentId"},"filename":{"$ref":"#/components/schemas/AttachmentFilename","nullable":true},"size":{"$ref":"#/components/schemas/AttachmentSize"},"content_type":{"$ref":"#/components/schemas/AttachmentContentType","nullable":true},"content_disposition":{"$ref":"#/components/schemas/AttachmentContentDisposition","nullable":true},"content_id":{"$ref":"#/components/schemas/AttachmentContentId","nullable":true}},"required":["attachment_id","size"]},"AttachmentResponse":{"title":"AttachmentResponse","type":"object","properties":{"attachment_id":{"$ref":"#/components/schemas/AttachmentId"},"filename":{"$ref":"#/components/schemas/AttachmentFilename","nullable":true},"size":{"$ref":"#/components/schemas/AttachmentSize"},"content_type":{"$ref":"#/components/schemas/AttachmentContentType","nullable":true},"content_disposition":{"$ref":"#/components/schemas/AttachmentContentDisposition","nullable":true},"content_id":{"$ref":"#/components/schemas/AttachmentContentId","nullable":true},"download_url":{"type":"string","description":"URL to download the attachment."},"expires_at":{"type":"string","format":"date-time","description":"Time at which the download URL expires."}},"required":["attachment_id","size","download_url","expires_at"]},"SendAttachment":{"title":"SendAttachment","type":"object","description":"Provide either `content` or `url` for each attachment.","properties":{"filename":{"$ref":"#/components/schemas/AttachmentFilename","nullable":true},"content_type":{"$ref":"#/components/schemas/AttachmentContentType","nullable":true},"content_disposition":{"$ref":"#/components/schemas/AttachmentContentDisposition","nullable":true},"content_id":{"$ref":"#/components/schemas/AttachmentContentId","nullable":true},"content":{"type":"string","nullable":true,"description":"Base64 encoded content of the attachment. The entire request, including the message body and all attachments, is limited to 6 MB."},"url":{"type":"string","nullable":true,"description":"URL that AgentMail can download without custom authentication headers or cookies.\nRedirects and pre-signed URLs are supported, and the final response must be a\nsuccessful 2xx response. Keep URL-backed attachments around 30 MB total per message."}}},"ScopeType":{"title":"ScopeType","type":"string","enum":["organization","pod","inbox"],"description":"The scope tier the authenticated credential is bound to."},"Identity":{"title":"Identity","type":"object","description":"Identity and scope of the authenticated credential.","properties":{"scope_type":{"$ref":"#/components/schemas/ScopeType"},"scope_id":{"type":"string","description":"ID of the most specific scope the credential is bound to.\nEquals inbox_id when scope_type is inbox, pod_id when pod, organization_id when organization."},"organization_id":{"$ref":"#/components/schemas/OrganizationId"},"pod_id":{"type":"string","nullable":true,"description":"ID of the pod the credential is scoped to. Present when scope_type is pod or inbox."},"inbox_id":{"type":"string","nullable":true,"description":"ID of the inbox the credential is scoped to. Present when scope_type is inbox."},"api_key_id":{"type":"string","nullable":true,"description":"ID of the API key used to authenticate. Absent for JWT and proxy credentials."}},"required":["scope_type","scope_id","organization_id"]},"DomainId":{"title":"DomainId","type":"string","description":"The ID of the domain."},"DomainName":{"title":"DomainName","type":"string","description":"The name of the domain (e.g., `example.com`)."},"RecordType":{"title":"RecordType","type":"string","enum":["TXT","CNAME","MX"]},"VerificationStatus":{"title":"VerificationStatus","type":"string","enum":["NOT_STARTED","PENDING","INVALID","FAILED","VERIFYING","VERIFIED"]},"RecordStatus":{"title":"RecordStatus","type":"string","enum":["MISSING","INVALID","VALID"]},"VerificationRecord":{"title":"VerificationRecord","type":"object","properties":{"type":{"$ref":"#/components/schemas/RecordType","description":"The type of the DNS record."},"name":{"type":"string","description":"The name or host of the record."},"value":{"type":"string","description":"The value of the record."},"status":{"$ref":"#/components/schemas/RecordStatus","description":"The verification status of this specific record."},"priority":{"type":"integer","nullable":true,"description":"The priority of the MX record."}},"required":["type","name","value","status"]},"Status":{"title":"Status","$ref":"#/components/schemas/VerificationStatus","description":"The verification status of the domain."},"FeedbackEnabled":{"title":"FeedbackEnabled","type":"boolean","description":"Bounce and complaint notifications are sent to your inboxes."},"SubdomainsEnabled":{"title":"SubdomainsEnabled","type":"boolean","description":"Allow inboxes on any subdomain of this domain. Adds a required wildcard MX\nrecord (`*.`) to `records`."},"TrackingEnabled":{"title":"TrackingEnabled","type":"boolean","description":"Serve open tracking pixels from this domain. Adds a required `link.`\nCNAME record to `records`, which must be published and verified before\n`track_opens` can be used on a send."},"ClientId":{"title":"ClientId","type":"string","description":"Client ID of domain."},"Domain":{"title":"Domain","type":"object","properties":{"pod_id":{"$ref":"#/components/schemas/podsPodId","nullable":true},"domain_id":{"$ref":"#/components/schemas/DomainId"},"domain":{"$ref":"#/components/schemas/DomainName"},"status":{"$ref":"#/components/schemas/Status"},"feedback_enabled":{"$ref":"#/components/schemas/FeedbackEnabled"},"subdomains_enabled":{"$ref":"#/components/schemas/SubdomainsEnabled"},"tracking_enabled":{"$ref":"#/components/schemas/TrackingEnabled"},"records":{"type":"array","items":{"$ref":"#/components/schemas/VerificationRecord"},"description":"A list of DNS records required to verify the domain. Includes a\nwildcard MX record (`*.`) when `subdomains_enabled` is true."},"client_id":{"$ref":"#/components/schemas/ClientId","nullable":true},"updated_at":{"type":"string","format":"date-time","description":"Time at which the domain was last updated."},"created_at":{"type":"string","format":"date-time","description":"Time at which the domain was created."}},"required":["domain_id","domain","status","feedback_enabled","subdomains_enabled","tracking_enabled","records","updated_at","created_at"]},"DomainItem":{"title":"DomainItem","type":"object","properties":{"pod_id":{"$ref":"#/components/schemas/podsPodId","nullable":true},"domain_id":{"$ref":"#/components/schemas/DomainId"},"domain":{"$ref":"#/components/schemas/DomainName"},"feedback_enabled":{"$ref":"#/components/schemas/FeedbackEnabled"},"subdomains_enabled":{"$ref":"#/components/schemas/SubdomainsEnabled"},"tracking_enabled":{"$ref":"#/components/schemas/TrackingEnabled"},"client_id":{"$ref":"#/components/schemas/ClientId","nullable":true},"updated_at":{"type":"string","format":"date-time","description":"Time at which the domain was last updated."},"created_at":{"type":"string","format":"date-time","description":"Time at which the domain was created."}},"required":["domain_id","domain","feedback_enabled","subdomains_enabled","tracking_enabled","updated_at","created_at"]},"ListDomainsResponse":{"title":"ListDomainsResponse","type":"object","properties":{"count":{"$ref":"#/components/schemas/Count"},"limit":{"$ref":"#/components/schemas/Limit","nullable":true},"next_page_token":{"$ref":"#/components/schemas/PageToken","nullable":true},"domains":{"type":"array","items":{"$ref":"#/components/schemas/DomainItem"},"description":"Ordered by `created_at` descending."}},"required":["count","domains"]},"CreateDomainRequest":{"title":"CreateDomainRequest","type":"object","properties":{"domain":{"$ref":"#/components/schemas/DomainName"},"feedback_enabled":{"$ref":"#/components/schemas/FeedbackEnabled","nullable":true},"subdomains_enabled":{"$ref":"#/components/schemas/SubdomainsEnabled","nullable":true},"tracking_enabled":{"$ref":"#/components/schemas/TrackingEnabled","nullable":true}},"required":["domain"]},"UpdateDomainRequest":{"title":"UpdateDomainRequest","type":"object","description":"Provide at least one of `feedback_enabled`, `subdomains_enabled`, or\n`tracking_enabled`. Omitted\nfields are left unchanged; an empty body is rejected. Enabling\n`subdomains_enabled` on a verified domain returns it to `PENDING` until the\nnewly-required wildcard MX record (`*.`) is published and verified.","properties":{"feedback_enabled":{"$ref":"#/components/schemas/FeedbackEnabled","nullable":true},"subdomains_enabled":{"$ref":"#/components/schemas/SubdomainsEnabled","nullable":true},"tracking_enabled":{"$ref":"#/components/schemas/TrackingEnabled","nullable":true}}},"DraftId":{"title":"DraftId","type":"string","description":"ID of draft."},"DraftClientId":{"title":"DraftClientId","type":"string","description":"Client ID of draft."},"DraftLabels":{"title":"DraftLabels","type":"array","items":{"type":"string"},"description":"Labels of draft."},"DraftReplyTo":{"title":"DraftReplyTo","type":"array","items":{"type":"string"},"description":"Reply-to addresses. In format `username@domain.com` or `Display Name `."},"DraftTo":{"title":"DraftTo","type":"array","items":{"type":"string"},"description":"Addresses of recipients. In format `username@domain.com` or `Display Name `."},"DraftCc":{"title":"DraftCc","type":"array","items":{"type":"string"},"description":"Addresses of CC recipients. In format `username@domain.com` or `Display Name `."},"DraftBcc":{"title":"DraftBcc","type":"array","items":{"type":"string"},"description":"Addresses of BCC recipients. In format `username@domain.com` or `Display Name `."},"DraftSubject":{"title":"DraftSubject","type":"string","description":"Subject of draft."},"DraftPreview":{"title":"DraftPreview","type":"string","description":"Text preview of draft."},"DraftText":{"title":"DraftText","type":"string","description":"Plain text body of draft."},"DraftHtml":{"title":"DraftHtml","type":"string","description":"HTML body of draft."},"DraftAttachments":{"title":"DraftAttachments","type":"array","items":{"$ref":"#/components/schemas/Attachment"},"description":"Attachments in draft."},"DraftInReplyTo":{"title":"DraftInReplyTo","type":"string","description":"ID of message being replied to."},"DraftForwardOf":{"title":"DraftForwardOf","type":"string","description":"ID of message being forwarded."},"DraftReplyAll":{"title":"DraftReplyAll","type":"boolean","description":"Reply to all recipients of the original message."},"DraftSendStatus":{"title":"DraftSendStatus","type":"string","enum":["scheduled","sending","failed"],"description":"Schedule send status of draft."},"DraftSendAt":{"title":"DraftSendAt","type":"string","format":"date-time","description":"Time at which to schedule send draft."},"DraftUpdatedAt":{"title":"DraftUpdatedAt","type":"string","format":"date-time","description":"Time at which draft was last updated."},"DraftItem":{"title":"DraftItem","type":"object","properties":{"inbox_id":{"$ref":"#/components/schemas/inboxesInboxId"},"draft_id":{"$ref":"#/components/schemas/DraftId"},"labels":{"$ref":"#/components/schemas/DraftLabels"},"to":{"$ref":"#/components/schemas/DraftTo","nullable":true},"cc":{"$ref":"#/components/schemas/DraftCc","nullable":true},"bcc":{"$ref":"#/components/schemas/DraftBcc","nullable":true},"subject":{"$ref":"#/components/schemas/DraftSubject","nullable":true},"preview":{"$ref":"#/components/schemas/DraftPreview","nullable":true},"attachments":{"$ref":"#/components/schemas/DraftAttachments","nullable":true},"in_reply_to":{"$ref":"#/components/schemas/DraftInReplyTo","nullable":true},"forward_of":{"$ref":"#/components/schemas/DraftForwardOf","nullable":true},"send_status":{"$ref":"#/components/schemas/DraftSendStatus","nullable":true},"send_at":{"$ref":"#/components/schemas/DraftSendAt","nullable":true},"updated_at":{"$ref":"#/components/schemas/DraftUpdatedAt"}},"required":["inbox_id","draft_id","labels","updated_at"]},"Draft":{"title":"Draft","type":"object","properties":{"inbox_id":{"$ref":"#/components/schemas/inboxesInboxId"},"draft_id":{"$ref":"#/components/schemas/DraftId"},"client_id":{"$ref":"#/components/schemas/DraftClientId","nullable":true},"labels":{"$ref":"#/components/schemas/DraftLabels"},"reply_to":{"$ref":"#/components/schemas/DraftReplyTo","nullable":true},"to":{"$ref":"#/components/schemas/DraftTo","nullable":true},"cc":{"$ref":"#/components/schemas/DraftCc","nullable":true},"bcc":{"$ref":"#/components/schemas/DraftBcc","nullable":true},"subject":{"$ref":"#/components/schemas/DraftSubject","nullable":true},"preview":{"$ref":"#/components/schemas/DraftPreview","nullable":true},"text":{"$ref":"#/components/schemas/DraftText","nullable":true},"html":{"$ref":"#/components/schemas/DraftHtml","nullable":true},"attachments":{"$ref":"#/components/schemas/DraftAttachments","nullable":true},"in_reply_to":{"$ref":"#/components/schemas/DraftInReplyTo","nullable":true},"forward_of":{"$ref":"#/components/schemas/DraftForwardOf","nullable":true},"references":{"type":"array","items":{"type":"string"},"nullable":true,"description":"IDs of previous messages in thread."},"send_status":{"$ref":"#/components/schemas/DraftSendStatus","nullable":true},"send_at":{"$ref":"#/components/schemas/DraftSendAt","nullable":true},"updated_at":{"$ref":"#/components/schemas/DraftUpdatedAt"},"created_at":{"type":"string","format":"date-time","description":"Time at which draft was created."}},"required":["inbox_id","draft_id","labels","updated_at","created_at"]},"ListDraftsResponse":{"title":"ListDraftsResponse","type":"object","properties":{"count":{"$ref":"#/components/schemas/Count"},"limit":{"$ref":"#/components/schemas/Limit","nullable":true},"next_page_token":{"$ref":"#/components/schemas/PageToken","nullable":true},"drafts":{"type":"array","items":{"$ref":"#/components/schemas/DraftItem"},"description":"Ordered by `updated_at` descending."}},"required":["count","drafts"]},"CreateDraftRequest":{"title":"CreateDraftRequest","type":"object","description":"Body for creating a draft. Supports plain, reply, reply-all, and forward\ndrafts:\n\n- **Plain draft:** supply `to`, `subject`, `text`, etc.\n- **Reply:** set `in_reply_to` to a message ID. Recipients, subject, and\n threading are derived from that message. Set `reply_all` to address the\n whole thread (you then cannot also pass `to`, `cc`, or `bcc`).\n- **Forward:** set `forward_of` to a message ID. The subject and threading\n are derived from the source message, whose body and attachments are\n merged in at send time.\n\n`in_reply_to` and `forward_of` are mutually exclusive, and reading the\nreferenced message requires `message_read` permission.","properties":{"labels":{"$ref":"#/components/schemas/DraftLabels","nullable":true},"reply_to":{"$ref":"#/components/schemas/DraftReplyTo","nullable":true},"to":{"$ref":"#/components/schemas/DraftTo","nullable":true},"cc":{"$ref":"#/components/schemas/DraftCc","nullable":true},"bcc":{"$ref":"#/components/schemas/DraftBcc","nullable":true},"subject":{"$ref":"#/components/schemas/DraftSubject","nullable":true},"text":{"$ref":"#/components/schemas/DraftText","nullable":true},"html":{"$ref":"#/components/schemas/DraftHtml","nullable":true},"attachments":{"type":"array","items":{"$ref":"#/components/schemas/SendAttachment"},"nullable":true,"description":"Attachments to include in draft."},"in_reply_to":{"$ref":"#/components/schemas/DraftInReplyTo","nullable":true},"forward_of":{"$ref":"#/components/schemas/DraftForwardOf","nullable":true},"reply_all":{"$ref":"#/components/schemas/DraftReplyAll","nullable":true},"send_at":{"$ref":"#/components/schemas/DraftSendAt","nullable":true},"client_id":{"$ref":"#/components/schemas/DraftClientId","nullable":true}}},"UpdateDraftRequest":{"title":"UpdateDraftRequest","type":"object","description":"Edit fields on an existing draft. A draft's kind (plain, reply, or forward)\nis fixed at creation and cannot be changed here. Omitting a field leaves it\nunchanged; passing `null` (or `[]` for a recipient field) clears it. Pass\n`send_at` to schedule or reschedule the draft, or `null` to un-schedule it.","properties":{"reply_to":{"$ref":"#/components/schemas/DraftReplyTo","nullable":true},"to":{"$ref":"#/components/schemas/DraftTo","nullable":true},"cc":{"$ref":"#/components/schemas/DraftCc","nullable":true},"bcc":{"$ref":"#/components/schemas/DraftBcc","nullable":true},"subject":{"$ref":"#/components/schemas/DraftSubject","nullable":true},"text":{"$ref":"#/components/schemas/DraftText","nullable":true},"html":{"$ref":"#/components/schemas/DraftHtml","nullable":true},"add_attachments":{"type":"array","items":{"$ref":"#/components/schemas/SendAttachment"},"nullable":true,"description":"Attachments to add to the draft."},"remove_attachments":{"type":"array","items":{"$ref":"#/components/schemas/AttachmentId"},"nullable":true,"description":"IDs of attachments to remove from the draft."},"add_labels":{"$ref":"#/components/schemas/DraftLabels","nullable":true,"description":"Label or labels to add to the draft."},"remove_labels":{"$ref":"#/components/schemas/DraftLabels","nullable":true,"description":"Label or labels to remove from the draft."},"send_at":{"$ref":"#/components/schemas/DraftSendAt","nullable":true}}},"EventType":{"title":"EventType","type":"string","enum":["message.received","message.received.spam","message.received.blocked","message.received.unauthenticated","message.sent","message.delivered","message.bounced","message.complained","message.rejected","message.opened","domain.verified"]},"EventTypes":{"title":"EventTypes","type":"array","items":{"$ref":"#/components/schemas/EventType"},"description":"Event types for which to send events."},"MessageReceivedEventType":{"title":"MessageReceivedEventType","type":"string","enum":["message.received","message.received.spam","message.received.blocked","message.received.unauthenticated"]},"PodIds":{"title":"PodIds","type":"array","items":{"type":"string"},"description":"Pods for which to send events. Maximum 10 per webhook."},"InboxIds":{"title":"InboxIds","type":"array","items":{"type":"string"},"description":"Inboxes for which to send events. Maximum 10 per webhook."},"EventId":{"title":"EventId","type":"string","description":"ID of event."},"Timestamp":{"title":"Timestamp","type":"string","format":"date-time","description":"Timestamp of event."},"Recipient":{"title":"Recipient","type":"object","properties":{"address":{"type":"string","description":"Recipient address."},"status":{"type":"string","description":"Recipient status."}},"required":["address","status"]},"Send":{"title":"Send","type":"object","properties":{"inbox_id":{"$ref":"#/components/schemas/inboxesInboxId"},"thread_id":{"$ref":"#/components/schemas/ThreadId"},"message_id":{"$ref":"#/components/schemas/MessageId"},"timestamp":{"$ref":"#/components/schemas/Timestamp"},"recipients":{"type":"array","items":{"type":"string"},"description":"Sent recipients."}},"required":["inbox_id","thread_id","message_id","timestamp","recipients"],"x-fern-type-name":"SendEvent"},"Delivery":{"title":"Delivery","type":"object","properties":{"inbox_id":{"$ref":"#/components/schemas/inboxesInboxId"},"thread_id":{"$ref":"#/components/schemas/ThreadId"},"message_id":{"$ref":"#/components/schemas/MessageId"},"timestamp":{"$ref":"#/components/schemas/Timestamp"},"recipients":{"type":"array","items":{"type":"string"},"description":"Delivered recipients."}},"required":["inbox_id","thread_id","message_id","timestamp","recipients"]},"Bounce":{"title":"Bounce","type":"object","properties":{"inbox_id":{"$ref":"#/components/schemas/inboxesInboxId"},"thread_id":{"$ref":"#/components/schemas/ThreadId"},"message_id":{"$ref":"#/components/schemas/MessageId"},"timestamp":{"$ref":"#/components/schemas/Timestamp"},"type":{"type":"string","description":"Bounce type."},"sub_type":{"type":"string","description":"Bounce sub-type."},"recipients":{"type":"array","items":{"$ref":"#/components/schemas/Recipient"},"description":"Bounced recipients."}},"required":["inbox_id","thread_id","message_id","timestamp","type","sub_type","recipients"]},"Complaint":{"title":"Complaint","type":"object","properties":{"inbox_id":{"$ref":"#/components/schemas/inboxesInboxId"},"thread_id":{"$ref":"#/components/schemas/ThreadId"},"message_id":{"$ref":"#/components/schemas/MessageId"},"timestamp":{"$ref":"#/components/schemas/Timestamp"},"type":{"type":"string","description":"Complaint type."},"sub_type":{"type":"string","description":"Complaint sub-type."},"recipients":{"type":"array","items":{"type":"string"},"description":"Complained recipients."}},"required":["inbox_id","thread_id","message_id","timestamp","type","sub_type","recipients"]},"Reject":{"title":"Reject","type":"object","properties":{"inbox_id":{"$ref":"#/components/schemas/inboxesInboxId"},"thread_id":{"$ref":"#/components/schemas/ThreadId"},"message_id":{"$ref":"#/components/schemas/MessageId"},"timestamp":{"$ref":"#/components/schemas/Timestamp"},"reason":{"type":"string","description":"Reject reason."}},"required":["inbox_id","thread_id","message_id","timestamp","reason"]},"Open":{"title":"Open","type":"object","properties":{"inbox_id":{"$ref":"#/components/schemas/inboxesInboxId"},"thread_id":{"$ref":"#/components/schemas/ThreadId"},"message_id":{"$ref":"#/components/schemas/MessageId"},"timestamp":{"$ref":"#/components/schemas/Timestamp"}},"required":["inbox_id","thread_id","message_id","timestamp"]},"MessageReceivedEvent":{"title":"MessageReceivedEvent","type":"object","description":"A message was received. Spam, blocked, and unauthenticated received-message events use the same payload shape with different `event_type` values.","properties":{"type":{"type":"string","const":"event"},"event_type":{"$ref":"#/components/schemas/MessageReceivedEventType"},"event_id":{"$ref":"#/components/schemas/EventId"},"message":{"$ref":"#/components/schemas/Message"},"thread":{"$ref":"#/components/schemas/ThreadItem"}},"required":["type","event_type","event_id","message","thread"]},"MessageSentEvent":{"title":"MessageSentEvent","type":"object","properties":{"type":{"type":"string","const":"event"},"event_type":{"type":"string","const":"message.sent"},"event_id":{"$ref":"#/components/schemas/EventId"},"send":{"$ref":"#/components/schemas/Send"}},"required":["type","event_type","event_id","send"]},"MessageDeliveredEvent":{"title":"MessageDeliveredEvent","type":"object","properties":{"type":{"type":"string","const":"event"},"event_type":{"type":"string","const":"message.delivered"},"event_id":{"$ref":"#/components/schemas/EventId"},"delivery":{"$ref":"#/components/schemas/Delivery"}},"required":["type","event_type","event_id","delivery"]},"MessageBouncedEvent":{"title":"MessageBouncedEvent","type":"object","properties":{"type":{"type":"string","const":"event"},"event_type":{"type":"string","const":"message.bounced"},"event_id":{"$ref":"#/components/schemas/EventId"},"bounce":{"$ref":"#/components/schemas/Bounce"}},"required":["type","event_type","event_id","bounce"]},"MessageComplainedEvent":{"title":"MessageComplainedEvent","type":"object","properties":{"type":{"type":"string","const":"event"},"event_type":{"type":"string","const":"message.complained"},"event_id":{"$ref":"#/components/schemas/EventId"},"complaint":{"$ref":"#/components/schemas/Complaint"}},"required":["type","event_type","event_id","complaint"]},"MessageRejectedEvent":{"title":"MessageRejectedEvent","type":"object","properties":{"type":{"type":"string","const":"event"},"event_type":{"type":"string","const":"message.rejected"},"event_id":{"$ref":"#/components/schemas/EventId"},"reject":{"$ref":"#/components/schemas/Reject"}},"required":["type","event_type","event_id","reject"]},"MessageOpenedEvent":{"title":"MessageOpenedEvent","type":"object","description":"A tracked message was opened for the first time. Sent once per message: repeat opens do not\nresend it.","properties":{"type":{"type":"string","const":"event"},"event_type":{"type":"string","const":"message.opened"},"event_id":{"$ref":"#/components/schemas/EventId"},"open":{"$ref":"#/components/schemas/Open"}},"required":["type","event_type","event_id","open"]},"DomainVerifiedEvent":{"title":"DomainVerifiedEvent","type":"object","properties":{"type":{"type":"string","const":"event"},"event_type":{"type":"string","const":"domain.verified"},"event_id":{"$ref":"#/components/schemas/EventId"},"domain":{"$ref":"#/components/schemas/Domain"}},"required":["type","event_type","event_id","domain"]},"InboxEventId":{"title":"InboxEventId","type":"string","description":"ID of event."},"InboxEventType":{"title":"InboxEventType","type":"string","enum":["label.added","label.removed"],"description":"Type of inbox event. Wire format is dot.case to match the\nconvention used by webhook events (`message.received`,\n`domain.verified`, etc. in events.yml). Pre-2026-04 these were\n`label_added`/`label_removed` (snake_case). The Fern enum's `name`\nfield stays uppercase-snake (Fern convention); only the wire\n`value` changed."},"InboxEvent":{"title":"InboxEvent","type":"object","properties":{"organization_id":{"$ref":"#/components/schemas/OrganizationId"},"pod_id":{"type":"string","description":"ID of pod."},"inbox_id":{"$ref":"#/components/schemas/inboxesInboxId"},"event_id":{"$ref":"#/components/schemas/InboxEventId"},"event_type":{"$ref":"#/components/schemas/InboxEventType"},"message_id":{"type":"string","description":"ID of message."},"label":{"type":"string","description":"Label added or removed."},"event_at":{"type":"string","format":"date-time","description":"Time at which the event occurred."},"created_at":{"type":"string","format":"date-time","description":"Time at which the event was recorded."}},"required":["organization_id","pod_id","inbox_id","event_id","event_type","message_id","label","event_at","created_at"]},"ListInboxEventsResponse":{"title":"ListInboxEventsResponse","type":"object","properties":{"count":{"$ref":"#/components/schemas/Count"},"limit":{"$ref":"#/components/schemas/Limit","nullable":true},"next_page_token":{"$ref":"#/components/schemas/PageToken","nullable":true},"events":{"type":"array","items":{"$ref":"#/components/schemas/InboxEvent"},"description":"Ordered by `event_id` descending."}},"required":["count","events"]},"Direction":{"title":"Direction","type":"string","enum":["send","receive","reply"],"description":"Direction of list entry."},"ListType":{"title":"ListType","type":"string","enum":["allow","block"],"description":"Type of list entry."},"EntryType":{"title":"EntryType","type":"string","enum":["email","domain"],"description":"Whether the entry is an email address or domain."},"ListEntryBase":{"title":"ListEntryBase","type":"object","properties":{"entry":{"type":"string","description":"Email address or domain of list entry."},"organization_id":{"$ref":"#/components/schemas/OrganizationId"},"reason":{"type":"string","nullable":true,"description":"Reason for adding the entry."},"direction":{"$ref":"#/components/schemas/Direction"},"list_type":{"$ref":"#/components/schemas/ListType"},"entry_type":{"$ref":"#/components/schemas/EntryType"},"created_at":{"type":"string","format":"date-time","description":"Time at which entry was created."},"read_only":{"type":"boolean","nullable":true,"description":"Whether the entry is read-only and cannot be deleted via the API."}},"required":["entry","organization_id","direction","list_type","entry_type","created_at"]},"ListEntry":{"title":"ListEntry","type":"object","properties":{},"allOf":[{"$ref":"#/components/schemas/ListEntryBase"}]},"PodListEntry":{"title":"PodListEntry","type":"object","properties":{"pod_id":{"type":"string","description":"ID of pod."},"inbox_id":{"type":"string","nullable":true,"description":"ID of inbox, if entry is inbox-scoped."}},"required":["pod_id"],"allOf":[{"$ref":"#/components/schemas/ListEntryBase"}]},"PodListListEntriesResponse":{"title":"PodListListEntriesResponse","type":"object","properties":{"count":{"$ref":"#/components/schemas/Count"},"limit":{"$ref":"#/components/schemas/Limit","nullable":true},"next_page_token":{"$ref":"#/components/schemas/PageToken","nullable":true},"entries":{"type":"array","items":{"$ref":"#/components/schemas/PodListEntry"},"description":"Ordered by entry ascending."}},"required":["count","entries"]},"ListListEntriesResponse":{"title":"ListListEntriesResponse","type":"object","properties":{"count":{"$ref":"#/components/schemas/Count"},"limit":{"$ref":"#/components/schemas/Limit","nullable":true},"next_page_token":{"$ref":"#/components/schemas/PageToken","nullable":true},"entries":{"type":"array","items":{"$ref":"#/components/schemas/ListEntry"},"description":"Ordered by entry ascending."}},"required":["count","entries"]},"CreateListEntryRequest":{"title":"CreateListEntryRequest","type":"object","properties":{"entry":{"type":"string","description":"Email address or domain to add."},"reason":{"type":"string","nullable":true,"description":"Reason for adding the entry."}},"required":["entry"]},"MessageId":{"title":"MessageId","type":"string","description":"ID of message."},"MessageLabels":{"title":"MessageLabels","type":"array","items":{"type":"string"},"description":"Labels of message."},"MessageTimestamp":{"title":"MessageTimestamp","type":"string","format":"date-time","description":"Time at which message was sent or drafted."},"MessageFrom":{"title":"MessageFrom","type":"string","description":"Address of sender. In format `username@domain.com` or `Display Name `."},"MessageReplyTo":{"title":"MessageReplyTo","type":"array","items":{"type":"string"},"description":"Addresses of reply-to recipients. In format `username@domain.com` or `Display Name `."},"MessageTo":{"title":"MessageTo","type":"array","items":{"type":"string"},"description":"Addresses of recipients. In format `username@domain.com` or `Display Name `."},"MessageCc":{"title":"MessageCc","type":"array","items":{"type":"string"},"description":"Addresses of CC recipients. In format `username@domain.com` or `Display Name `."},"MessageBcc":{"title":"MessageBcc","type":"array","items":{"type":"string"},"description":"Addresses of BCC recipients. In format `username@domain.com` or `Display Name `."},"MessageSubject":{"title":"MessageSubject","type":"string","description":"Subject of message."},"MessagePreview":{"title":"MessagePreview","type":"string","description":"Text preview of message."},"MessageText":{"title":"MessageText","type":"string","description":"Plain text body of message."},"MessageHtml":{"title":"MessageHtml","type":"string","description":"HTML body of message."},"MessageAttachments":{"title":"MessageAttachments","type":"array","items":{"$ref":"#/components/schemas/Attachment"},"description":"Attachments in message."},"MessageInReplyTo":{"title":"MessageInReplyTo","type":"string","description":"ID of message being replied to."},"MessageReferences":{"title":"MessageReferences","type":"array","items":{"type":"string"},"description":"IDs of previous messages in thread."},"MessageHeaders":{"title":"MessageHeaders","type":"object","additionalProperties":{"type":"string"},"description":"Headers in message."},"MessageSize":{"title":"MessageSize","type":"integer","description":"Size of message in bytes."},"MessageUpdatedAt":{"title":"MessageUpdatedAt","type":"string","format":"date-time","description":"Time at which message was last updated."},"MessageCreatedAt":{"title":"MessageCreatedAt","type":"string","format":"date-time","description":"Time at which message was created."},"MessageItem":{"title":"MessageItem","type":"object","properties":{"inbox_id":{"$ref":"#/components/schemas/inboxesInboxId"},"thread_id":{"$ref":"#/components/schemas/ThreadId"},"message_id":{"$ref":"#/components/schemas/MessageId"},"labels":{"$ref":"#/components/schemas/MessageLabels"},"timestamp":{"$ref":"#/components/schemas/MessageTimestamp"},"from":{"$ref":"#/components/schemas/MessageFrom"},"to":{"$ref":"#/components/schemas/MessageTo"},"cc":{"$ref":"#/components/schemas/MessageCc","nullable":true},"bcc":{"$ref":"#/components/schemas/MessageBcc","nullable":true},"subject":{"$ref":"#/components/schemas/MessageSubject","nullable":true},"preview":{"$ref":"#/components/schemas/MessagePreview","nullable":true},"attachments":{"$ref":"#/components/schemas/MessageAttachments","nullable":true},"in_reply_to":{"$ref":"#/components/schemas/MessageInReplyTo","nullable":true},"references":{"$ref":"#/components/schemas/MessageReferences","nullable":true},"headers":{"$ref":"#/components/schemas/MessageHeaders","nullable":true},"size":{"$ref":"#/components/schemas/MessageSize"},"updated_at":{"$ref":"#/components/schemas/MessageUpdatedAt"},"created_at":{"$ref":"#/components/schemas/MessageCreatedAt"}},"required":["inbox_id","thread_id","message_id","labels","timestamp","from","to","size","updated_at","created_at"]},"Message":{"title":"Message","type":"object","properties":{"inbox_id":{"$ref":"#/components/schemas/inboxesInboxId"},"thread_id":{"$ref":"#/components/schemas/ThreadId"},"message_id":{"$ref":"#/components/schemas/MessageId"},"labels":{"$ref":"#/components/schemas/MessageLabels"},"timestamp":{"$ref":"#/components/schemas/MessageTimestamp"},"from":{"$ref":"#/components/schemas/MessageFrom"},"reply_to":{"type":"array","items":{"type":"string"},"nullable":true,"description":"Reply-to addresses. In format `username@domain.com` or `Display Name `."},"to":{"$ref":"#/components/schemas/MessageTo"},"cc":{"$ref":"#/components/schemas/MessageCc","nullable":true},"bcc":{"$ref":"#/components/schemas/MessageBcc","nullable":true},"subject":{"$ref":"#/components/schemas/MessageSubject","nullable":true},"preview":{"$ref":"#/components/schemas/MessagePreview","nullable":true},"text":{"$ref":"#/components/schemas/MessageText","nullable":true},"html":{"$ref":"#/components/schemas/MessageHtml","nullable":true},"extracted_text":{"type":"string","nullable":true,"description":"Extracted new text content."},"extracted_html":{"type":"string","nullable":true,"description":"Extracted new HTML content."},"attachments":{"$ref":"#/components/schemas/MessageAttachments","nullable":true},"in_reply_to":{"$ref":"#/components/schemas/MessageInReplyTo","nullable":true},"references":{"$ref":"#/components/schemas/MessageReferences","nullable":true},"headers":{"$ref":"#/components/schemas/MessageHeaders","nullable":true},"size":{"$ref":"#/components/schemas/MessageSize"},"updated_at":{"$ref":"#/components/schemas/MessageUpdatedAt"},"created_at":{"$ref":"#/components/schemas/MessageCreatedAt"}},"required":["inbox_id","thread_id","message_id","labels","timestamp","from","to","size","updated_at","created_at"]},"ListMessagesResponse":{"title":"ListMessagesResponse","type":"object","properties":{"count":{"$ref":"#/components/schemas/Count"},"limit":{"$ref":"#/components/schemas/Limit","nullable":true},"next_page_token":{"$ref":"#/components/schemas/PageToken","nullable":true},"messages":{"type":"array","items":{"$ref":"#/components/schemas/MessageItem"},"description":"Ordered by `timestamp` descending."}},"required":["count","messages"]},"SearchMessageHighlights":{"title":"SearchMessageHighlights","type":"object","description":"Matched fragments per field on a message search result, with matched terms\nwrapped in `**`. A field key is present only when the query matched that\nfield, so the present keys also tell you which fields produced the hit.","properties":{"from":{"type":"array","items":{"type":"string"},"nullable":true,"description":"Matched fragments from the sender address."},"recipients":{"type":"array","items":{"type":"string"},"nullable":true,"description":"Matched fragments from the recipient addresses (to, cc, or bcc)."},"subject":{"type":"array","items":{"type":"string"},"nullable":true,"description":"Matched fragments from the subject."},"text":{"type":"array","items":{"type":"string"},"nullable":true,"description":"Matched fragments from the message body."}}},"SearchMessageItem":{"title":"SearchMessageItem","type":"object","properties":{"highlights":{"$ref":"#/components/schemas/SearchMessageHighlights","nullable":true,"description":"Matched fragments per field. Present only when the query matched an indexed field."}},"allOf":[{"$ref":"#/components/schemas/MessageItem"}]},"SearchMessagesResponse":{"title":"SearchMessagesResponse","type":"object","properties":{"count":{"$ref":"#/components/schemas/Count"},"limit":{"$ref":"#/components/schemas/Limit","nullable":true},"next_page_token":{"$ref":"#/components/schemas/PageToken","nullable":true},"messages":{"type":"array","items":{"$ref":"#/components/schemas/SearchMessageItem"},"description":"Ordered by relevance, best match first."}},"required":["count","messages"]},"BatchGetMessagesMessageIds":{"title":"BatchGetMessagesMessageIds","type":"array","items":{"$ref":"#/components/schemas/MessageId"},"description":"IDs of messages to fetch. Maximum 500 ids per request. Duplicates are\nrejected with a validation error. IDs not found in the inbox (including\ncross-inbox or permission-restricted) are silently omitted from the\nresponse; callers detect misses by comparing `count` against `limit`."},"BatchGetMessagesRequest":{"title":"BatchGetMessagesRequest","type":"object","properties":{"message_ids":{"$ref":"#/components/schemas/BatchGetMessagesMessageIds"}},"required":["message_ids"]},"BatchGetMessagesResponse":{"title":"BatchGetMessagesResponse","type":"object","properties":{"limit":{"$ref":"#/components/schemas/Limit"},"count":{"$ref":"#/components/schemas/Count"},"messages":{"type":"array","items":{"$ref":"#/components/schemas/Message"},"description":"Found messages. Order matches `message_ids` in the request. Body\nfields (`text`, `html`, `extracted_text`, `extracted_html`) are\nnever populated; use the single-message endpoint to retrieve bodies."}},"required":["limit","count","messages"]},"BatchUpdateMessagesMessageIds":{"title":"BatchUpdateMessagesMessageIds","type":"array","items":{"$ref":"#/components/schemas/MessageId"},"description":"IDs of messages to update. Maximum 50 ids per request. Duplicates are\nrejected with a validation error. IDs not found in the inbox (including\ncross-inbox or permission-restricted) are silently excluded from the\nupdate; callers detect exclusions by comparing `count` against `limit`."},"BatchUpdateMessagesRequest":{"title":"BatchUpdateMessagesRequest","type":"object","properties":{"message_ids":{"$ref":"#/components/schemas/BatchUpdateMessagesMessageIds"},"add_labels":{"$ref":"#/components/schemas/UpdateMessageLabels","nullable":true,"description":"Label or labels to add to every message."},"remove_labels":{"$ref":"#/components/schemas/UpdateMessageLabels","nullable":true,"description":"Label or labels to remove from every message."}},"required":["message_ids"]},"BatchUpdateMessagesResponse":{"title":"BatchUpdateMessagesResponse","type":"object","properties":{"limit":{"$ref":"#/components/schemas/Limit"},"count":{"$ref":"#/components/schemas/Count"},"updates":{"type":"array","items":{"$ref":"#/components/schemas/UpdateMessageResponse"},"description":"Updated messages with their new labels. Order matches `message_ids`\nin the request. Excluded ids are omitted, so `count` may be less than\n`limit`."}},"required":["limit","count","updates"]},"RawMessageResponse":{"title":"RawMessageResponse","type":"object","description":"S3 presigned URL to download the raw .eml file.","properties":{"message_id":{"$ref":"#/components/schemas/MessageId","description":"ID of the message."},"size":{"$ref":"#/components/schemas/MessageSize","description":"Size of the raw message in bytes."},"download_url":{"type":"string","description":"S3 presigned URL to download the raw message. Expires at expires_at."},"expires_at":{"type":"string","format":"date-time","description":"Time at which the download URL expires."}},"required":["message_id","size","download_url","expires_at"]},"Addresses":{"title":"Addresses","oneOf":[{"type":"string"},{"type":"array","items":{"type":"string"}}]},"SendMessageReplyTo":{"title":"SendMessageReplyTo","$ref":"#/components/schemas/Addresses","description":"Reply-to address or addresses."},"SendMessageTo":{"title":"SendMessageTo","$ref":"#/components/schemas/Addresses","description":"Recipient address or addresses."},"SendMessageCc":{"title":"SendMessageCc","$ref":"#/components/schemas/Addresses","description":"CC recipient address or addresses."},"SendMessageBcc":{"title":"SendMessageBcc","$ref":"#/components/schemas/Addresses","description":"BCC recipient address or addresses."},"SendMessageAttachments":{"title":"SendMessageAttachments","type":"array","items":{"$ref":"#/components/schemas/SendAttachment"},"description":"Attachments to include in message."},"SendMessageHeaders":{"title":"SendMessageHeaders","type":"object","additionalProperties":{"type":"string"},"description":"Headers to include in message."},"TrackOpens":{"title":"TrackOpens","type":"boolean","description":"Track when this message is first opened. Requires a custom domain with tracking enabled and\nan HTML body. Opens surface as the `opened` label on the message and as a `message.opened`\nevent. One pixel is injected per message, not per recipient, so a message with several\nrecipients fires once when any of them opens it, and the event does not identify which one."},"SendMessageRequest":{"title":"SendMessageRequest","type":"object","properties":{"labels":{"$ref":"#/components/schemas/MessageLabels","nullable":true},"reply_to":{"$ref":"#/components/schemas/SendMessageReplyTo","nullable":true},"to":{"$ref":"#/components/schemas/SendMessageTo","nullable":true},"cc":{"$ref":"#/components/schemas/SendMessageCc","nullable":true},"bcc":{"$ref":"#/components/schemas/SendMessageBcc","nullable":true},"subject":{"$ref":"#/components/schemas/MessageSubject","nullable":true},"text":{"$ref":"#/components/schemas/MessageText","nullable":true},"html":{"$ref":"#/components/schemas/MessageHtml","nullable":true},"attachments":{"$ref":"#/components/schemas/SendMessageAttachments","nullable":true},"headers":{"$ref":"#/components/schemas/SendMessageHeaders","nullable":true},"track_opens":{"$ref":"#/components/schemas/TrackOpens","nullable":true}}},"SendMessageResponse":{"title":"SendMessageResponse","type":"object","properties":{"message_id":{"$ref":"#/components/schemas/MessageId"},"thread_id":{"$ref":"#/components/schemas/ThreadId"}},"required":["message_id","thread_id"]},"UpdateMessageResponse":{"title":"UpdateMessageResponse","type":"object","properties":{"message_id":{"$ref":"#/components/schemas/MessageId"},"labels":{"$ref":"#/components/schemas/MessageLabels"}},"required":["message_id","labels"]},"ReplyAll":{"title":"ReplyAll","type":"boolean","description":"Reply to all recipients of the original message."},"ReplyToMessageRequest":{"title":"ReplyToMessageRequest","type":"object","properties":{"labels":{"$ref":"#/components/schemas/MessageLabels","nullable":true},"reply_to":{"$ref":"#/components/schemas/SendMessageReplyTo","nullable":true},"to":{"$ref":"#/components/schemas/SendMessageTo","nullable":true},"cc":{"$ref":"#/components/schemas/SendMessageCc","nullable":true},"bcc":{"$ref":"#/components/schemas/SendMessageBcc","nullable":true},"reply_all":{"$ref":"#/components/schemas/ReplyAll","nullable":true},"text":{"$ref":"#/components/schemas/MessageText","nullable":true},"html":{"$ref":"#/components/schemas/MessageHtml","nullable":true},"attachments":{"$ref":"#/components/schemas/SendMessageAttachments","nullable":true},"headers":{"$ref":"#/components/schemas/SendMessageHeaders","nullable":true},"track_opens":{"$ref":"#/components/schemas/TrackOpens","nullable":true}}},"ReplyAllMessageRequest":{"title":"ReplyAllMessageRequest","type":"object","properties":{"labels":{"$ref":"#/components/schemas/MessageLabels","nullable":true},"reply_to":{"$ref":"#/components/schemas/SendMessageReplyTo","nullable":true},"text":{"$ref":"#/components/schemas/MessageText","nullable":true},"html":{"$ref":"#/components/schemas/MessageHtml","nullable":true},"attachments":{"$ref":"#/components/schemas/SendMessageAttachments","nullable":true},"headers":{"$ref":"#/components/schemas/SendMessageHeaders","nullable":true},"track_opens":{"$ref":"#/components/schemas/TrackOpens","nullable":true}}},"UpdateMessageLabels":{"title":"UpdateMessageLabels","oneOf":[{"type":"string"},{"type":"array","items":{"type":"string"}}],"description":"Label or list of labels."},"UpdateMessageRequest":{"title":"UpdateMessageRequest","type":"object","properties":{"add_labels":{"$ref":"#/components/schemas/UpdateMessageLabels","nullable":true,"description":"Label or labels to add to message."},"remove_labels":{"$ref":"#/components/schemas/UpdateMessageLabels","nullable":true,"description":"Label or labels to remove from message."}}},"MetricEventType":{"title":"MetricEventType","type":"string","enum":["message.received","message.received.spam","message.received.blocked","message.received.unauthenticated","message.sent","message.delivered","message.bounced","message.complained","message.rejected","domain.verified"],"description":"Type of metric event."},"MetricEventTypes":{"title":"MetricEventTypes","type":"array","items":{"$ref":"#/components/schemas/MetricEventType"},"description":"List of metric event types to query."},"Start":{"title":"Start","type":"string","format":"date-time","description":"Start timestamp for the query."},"End":{"title":"End","type":"string","format":"date-time","description":"End timestamp for the query."},"Period":{"title":"Period","type":"integer","description":"Size of each time bucket as a whole number of seconds, between 1 and 86400."},"MetricLimit":{"title":"MetricLimit","type":"integer","description":"Limit on number of buckets to return."},"Descending":{"title":"Descending","type":"boolean","description":"Sort in descending order."},"MetricBucket":{"title":"MetricBucket","type":"object","properties":{"timestamp":{"type":"string","format":"date-time","description":"Timestamp of the bucket."},"count":{"type":"integer","description":"Count of events in the bucket."}},"required":["timestamp","count"]},"QueryMetricsResponse":{"title":"QueryMetricsResponse","type":"object","additionalProperties":{"type":"array","items":{"$ref":"#/components/schemas/MetricBucket"}},"description":"Metrics grouped by event type."},"UsageType":{"title":"UsageType","type":"string","enum":["storage_bytes","message_count","thread_count","inbox_count","pod_count","domain_count"],"description":"Type of usage metric. Inbox-scoped queries carry `storage_bytes`,\n`message_count`, and `thread_count`; pod-scoped queries add `inbox_count`\nand `domain_count`; organization-scoped queries add `pod_count`."},"UsageTypes":{"title":"UsageTypes","type":"array","items":{"$ref":"#/components/schemas/UsageType"},"description":"List of usage metric types to query. Omit to query every type valid for the scope."},"UsagePoint":{"title":"UsagePoint","type":"object","properties":{"timestamp":{"type":"string","format":"date-time","description":"Timestamp of the point."},"value":{"type":"integer","format":"int64","description":"Cumulative value of the usage metric at the timestamp."}},"required":["timestamp","value"]},"QueryUsageResponse":{"title":"QueryUsageResponse","type":"object","additionalProperties":{"type":"array","items":{"$ref":"#/components/schemas/UsagePoint"}},"description":"Cumulative usage series grouped by usage type."},"Organization":{"title":"Organization","type":"object","description":"Organization details with usage limits and counts.","properties":{"organization_id":{"$ref":"#/components/schemas/OrganizationId"},"inbox_count":{"type":"integer","description":"Current number of inboxes."},"domain_count":{"type":"integer","description":"Current number of domains."},"inbox_limit":{"type":"integer","nullable":true,"description":"Maximum number of inboxes allowed."},"domain_limit":{"type":"integer","nullable":true,"description":"Maximum number of domains allowed."},"billing_id":{"type":"string","nullable":true,"description":"Provider-agnostic billing customer ID."},"billing_type":{"type":"string","nullable":true,"description":"Billing provider type (e.g. \"stripe\")."},"billing_subscription_id":{"type":"string","nullable":true,"description":"Active billing subscription ID."},"authentication_id":{"type":"string","nullable":true,"description":"Provider-agnostic authentication ID."},"authentication_type":{"type":"string","nullable":true,"description":"Authentication provider type."},"updated_at":{"type":"string","format":"date-time","description":"Time at which organization was last updated."},"created_at":{"type":"string","format":"date-time","description":"Time at which organization was created."}},"required":["organization_id","inbox_count","domain_count","updated_at","created_at"]},"ThreadId":{"title":"ThreadId","type":"string","description":"ID of thread."},"ThreadLabels":{"title":"ThreadLabels","type":"array","items":{"type":"string"},"description":"Labels of thread."},"ThreadTimestamp":{"title":"ThreadTimestamp","type":"string","format":"date-time","description":"Timestamp of last sent or received message."},"ThreadReceivedTimestamp":{"title":"ThreadReceivedTimestamp","type":"string","format":"date-time","description":"Timestamp of last received message."},"ThreadSentTimestamp":{"title":"ThreadSentTimestamp","type":"string","format":"date-time","description":"Timestamp of last sent message."},"ThreadSenders":{"title":"ThreadSenders","type":"array","items":{"type":"string"},"description":"Senders in thread. In format `username@domain.com` or `Display Name `."},"ThreadRecipients":{"title":"ThreadRecipients","type":"array","items":{"type":"string"},"description":"Recipients in thread. In format `username@domain.com` or `Display Name `."},"ThreadSubject":{"title":"ThreadSubject","type":"string","description":"Subject of thread."},"ThreadPreview":{"title":"ThreadPreview","type":"string","description":"Text preview of last message in thread."},"ThreadAttachments":{"title":"ThreadAttachments","type":"array","items":{"$ref":"#/components/schemas/Attachment"},"description":"Attachments in thread."},"ThreadLastMessageId":{"title":"ThreadLastMessageId","type":"string","description":"ID of last message in thread."},"ThreadMessageCount":{"title":"ThreadMessageCount","type":"integer","description":"Number of messages in thread."},"ThreadSize":{"title":"ThreadSize","type":"integer","description":"Size of thread in bytes."},"ThreadUpdatedAt":{"title":"ThreadUpdatedAt","type":"string","format":"date-time","description":"Time at which thread was last updated."},"ThreadCreatedAt":{"title":"ThreadCreatedAt","type":"string","format":"date-time","description":"Time at which thread was created."},"ThreadItem":{"title":"ThreadItem","type":"object","properties":{"inbox_id":{"$ref":"#/components/schemas/inboxesInboxId"},"thread_id":{"$ref":"#/components/schemas/ThreadId"},"labels":{"$ref":"#/components/schemas/ThreadLabels"},"timestamp":{"$ref":"#/components/schemas/ThreadTimestamp"},"received_timestamp":{"$ref":"#/components/schemas/ThreadReceivedTimestamp","nullable":true},"sent_timestamp":{"$ref":"#/components/schemas/ThreadSentTimestamp","nullable":true},"senders":{"$ref":"#/components/schemas/ThreadSenders"},"recipients":{"$ref":"#/components/schemas/ThreadRecipients"},"subject":{"$ref":"#/components/schemas/ThreadSubject","nullable":true},"preview":{"$ref":"#/components/schemas/ThreadPreview","nullable":true},"attachments":{"$ref":"#/components/schemas/ThreadAttachments","nullable":true},"last_message_id":{"$ref":"#/components/schemas/ThreadLastMessageId"},"message_count":{"$ref":"#/components/schemas/ThreadMessageCount"},"size":{"$ref":"#/components/schemas/ThreadSize"},"updated_at":{"$ref":"#/components/schemas/ThreadUpdatedAt"},"created_at":{"$ref":"#/components/schemas/ThreadCreatedAt"}},"required":["inbox_id","thread_id","labels","timestamp","senders","recipients","last_message_id","message_count","size","updated_at","created_at"]},"Thread":{"title":"Thread","type":"object","properties":{"inbox_id":{"$ref":"#/components/schemas/inboxesInboxId"},"thread_id":{"$ref":"#/components/schemas/ThreadId"},"labels":{"$ref":"#/components/schemas/ThreadLabels"},"timestamp":{"$ref":"#/components/schemas/ThreadTimestamp"},"received_timestamp":{"$ref":"#/components/schemas/ThreadReceivedTimestamp","nullable":true},"sent_timestamp":{"$ref":"#/components/schemas/ThreadSentTimestamp","nullable":true},"senders":{"$ref":"#/components/schemas/ThreadSenders"},"recipients":{"$ref":"#/components/schemas/ThreadRecipients"},"subject":{"$ref":"#/components/schemas/ThreadSubject","nullable":true},"preview":{"$ref":"#/components/schemas/ThreadPreview","nullable":true},"attachments":{"$ref":"#/components/schemas/ThreadAttachments","nullable":true},"last_message_id":{"$ref":"#/components/schemas/ThreadLastMessageId"},"message_count":{"$ref":"#/components/schemas/ThreadMessageCount"},"size":{"$ref":"#/components/schemas/ThreadSize"},"updated_at":{"$ref":"#/components/schemas/ThreadUpdatedAt"},"created_at":{"$ref":"#/components/schemas/ThreadCreatedAt"},"messages":{"type":"array","items":{"$ref":"#/components/schemas/Message"},"description":"Messages in thread. Ordered by `timestamp` ascending."}},"required":["inbox_id","thread_id","labels","timestamp","senders","recipients","last_message_id","message_count","size","updated_at","created_at","messages"]},"UpdateThreadRequest":{"title":"UpdateThreadRequest","type":"object","properties":{"add_labels":{"type":"array","items":{"type":"string"},"nullable":true,"description":"Labels to add to thread. Cannot be system labels."},"remove_labels":{"type":"array","items":{"type":"string"},"nullable":true,"description":"Labels to remove from thread. Cannot be system labels. Takes priority over `add_labels` (in the event of duplicate labels passed in)."}}},"UpdateThreadResponse":{"title":"UpdateThreadResponse","type":"object","properties":{"thread_id":{"$ref":"#/components/schemas/ThreadId"},"labels":{"$ref":"#/components/schemas/ThreadLabels"}},"required":["thread_id","labels"]},"ListThreadsResponse":{"title":"ListThreadsResponse","type":"object","properties":{"count":{"$ref":"#/components/schemas/Count"},"limit":{"$ref":"#/components/schemas/Limit","nullable":true},"next_page_token":{"$ref":"#/components/schemas/PageToken","nullable":true},"threads":{"type":"array","items":{"$ref":"#/components/schemas/ThreadItem"},"description":"Ordered by `timestamp` descending."}},"required":["count","threads"]},"SearchThreadHighlights":{"title":"SearchThreadHighlights","type":"object","description":"Matched fragments per field on a thread search result, with matched terms\nwrapped in `**`. A field key is present only when the query matched that\nfield, so the present keys also tell you which fields produced the hit.","properties":{"from":{"type":"array","items":{"type":"string"},"nullable":true,"description":"Matched fragments from a sender address in the thread."},"recipients":{"type":"array","items":{"type":"string"},"nullable":true,"description":"Matched fragments from a recipient address in the thread (to, cc, or bcc)."},"subject":{"type":"array","items":{"type":"string"},"nullable":true,"description":"Matched fragments from the subject."},"text":{"type":"array","items":{"type":"string"},"nullable":true,"description":"Matched fragments from a message body in the thread."}}},"SearchThreadItem":{"title":"SearchThreadItem","type":"object","properties":{"highlights":{"$ref":"#/components/schemas/SearchThreadHighlights","nullable":true,"description":"Matched fragments per field. Present only when the query matched an indexed field."}},"allOf":[{"$ref":"#/components/schemas/ThreadItem"}]},"SearchThreadsResponse":{"title":"SearchThreadsResponse","type":"object","properties":{"count":{"$ref":"#/components/schemas/Count"},"limit":{"$ref":"#/components/schemas/Limit","nullable":true},"next_page_token":{"$ref":"#/components/schemas/PageToken","nullable":true},"threads":{"type":"array","items":{"$ref":"#/components/schemas/SearchThreadItem"},"description":"Ordered by relevance, best match first."}},"required":["count","threads"]},"webhooksSvixId":{"title":"webhooksSvixId","type":"string","description":"ID of webhook message."},"webhooksSvixTimestamp":{"title":"webhooksSvixTimestamp","type":"string","format":"date-time","description":"Timestamp of webhook message."},"webhooksSvixSignature":{"title":"webhooksSvixSignature","type":"string","description":"Signature of webhook message."},"Subscribe":{"title":"Subscribe","type":"object","properties":{"type":{"type":"string","const":"subscribe"},"event_types":{"$ref":"#/components/schemas/EventTypes","nullable":true},"inbox_ids":{"$ref":"#/components/schemas/InboxIds","nullable":true},"pod_ids":{"$ref":"#/components/schemas/PodIds","nullable":true}},"required":["type"]},"Subscribed":{"title":"Subscribed","type":"object","properties":{"type":{"type":"string","const":"subscribed"},"event_types":{"$ref":"#/components/schemas/EventTypes","nullable":true},"inbox_ids":{"$ref":"#/components/schemas/InboxIds","nullable":true},"pod_ids":{"$ref":"#/components/schemas/PodIds","nullable":true}},"required":["type"]},"Error":{"title":"Error","type":"object","properties":{"type":{"type":"string","const":"error"},"name":{"$ref":"#/components/schemas/ErrorName"},"message":{"$ref":"#/components/schemas/ErrorMessage"}},"required":["type","name","message"]}},"securitySchemes":{"BearerAuth":{"type":"http","scheme":"bearer"},"TokenAuth":{"type":"http","scheme":"bearer"}}}} \ No newline at end of file diff --git a/cli/agentmail/sdk.rs b/cli/agentmail/sdk.rs index 8ded18f..298c809 100644 --- a/cli/agentmail/sdk.rs +++ b/cli/agentmail/sdk.rs @@ -48,7 +48,14 @@ impl agentmail_sdk::RequestExecutor for CliExecutorAdapter { pub fn client(ctx: &AppContext) -> agentmail_sdk::api::ApiClient { let executor = ctx.build_sdk_executor(); let adapter = Arc::new(CliExecutorAdapter(executor)); - let config = agentmail_sdk::ClientConfig::default(); + // Seed the base URL from the CLI's own resolution (--base-url / env > + // spec base_url > server root). `ClientConfig::default()` carries an + // empty `base_url` for any API that declares no environment, which made + // every custom command fail on a relative URL before the executor ran. + let config = agentmail_sdk::ClientConfig { + base_url: ctx.effective_base_url(), + ..Default::default() + }; let http_client = agentmail_sdk::HttpClient::with_executor( adapter as Arc, config.clone(), @@ -58,6 +65,7 @@ pub fn client(ctx: &AppContext) -> agentmail_sdk::api::ApiClient { inboxes: agentmail_sdk::api::InboxesClient { http_client: http_client.clone(), api_keys: agentmail_sdk::api::resources::inboxes::ApiKeysClient2 { http_client: http_client.clone() }, + browser_credentials: agentmail_sdk::api::resources::inboxes::BrowserCredentialsClient { http_client: http_client.clone() }, drafts: agentmail_sdk::api::resources::inboxes::DraftsClient2 { http_client: http_client.clone() }, events: agentmail_sdk::api::resources::inboxes::EventsClient { http_client: http_client.clone() }, lists: agentmail_sdk::api::resources::inboxes::ListsClient2 { http_client: http_client.clone() }, @@ -110,11 +118,9 @@ where fn convert_api_error(e: agentmail_sdk::ApiError) -> CliError { match e { - agentmail_sdk::ApiError::Http { status, message } => CliError::Api { - code: status, - message, - reason: http_status_reason(status).to_string(), - }, + agentmail_sdk::ApiError::Http { status, message } => { + fern_cli_sdk::error::api_error_from_body(status, &message) + } agentmail_sdk::ApiError::Network(err) => { CliError::Other(anyhow::anyhow!("SDK network error: {err}")) } @@ -125,20 +131,3 @@ fn convert_api_error(e: agentmail_sdk::ApiError) -> CliError { other => CliError::Other(anyhow::anyhow!("SDK error: {other}")), } } - -fn http_status_reason(status: u16) -> &'static str { - match status { - 400 => "badRequest", - 401 => "unauthorized", - 403 => "forbidden", - 404 => "notFound", - 408 => "requestTimeout", - 409 => "conflict", - 429 => "tooManyRequests", - 500 => "internalServerError", - 502 => "badGateway", - 503 => "serviceUnavailable", - 504 => "gatewayTimeout", - _ => "httpError", - } -} diff --git a/dist-workspace.toml b/dist-workspace.toml index f220b5e..bc40181 100644 --- a/dist-workspace.toml +++ b/dist-workspace.toml @@ -17,7 +17,7 @@ installers = ["shell", "powershell"] # Whether to enable GitHub Attestations github-attestations = true # Target platforms to build apps for (Rust target-triple syntax) -targets = ["aarch64-apple-darwin", "aarch64-unknown-linux-gnu", "x86_64-apple-darwin", "x86_64-unknown-linux-gnu", "x86_64-unknown-linux-musl", "x86_64-pc-windows-msvc"] +targets = ["aarch64-apple-darwin", "aarch64-unknown-linux-gnu", "aarch64-unknown-linux-musl", "x86_64-apple-darwin", "x86_64-unknown-linux-gnu", "x86_64-unknown-linux-musl", "x86_64-pc-windows-msvc"] # Which actions to run on pull requests pr-run-mode = "plan" # Publish jobs to run (npm publishing deferred until pipeline is validated) diff --git a/reference.md b/reference.md index 0c969c1..b79795b 100644 --- a/reference.md +++ b/reference.md @@ -11,6 +11,7 @@ Full command reference for `agentmail`. - [`agentmail drafts`](#agentmail-drafts) - [`agentmail inboxes`](#agentmail-inboxes) - [`agentmail inboxes api-keys`](#agentmail-inboxes-api-keys) +- [`agentmail inboxes browser-credentials`](#agentmail-inboxes-browser-credentials) - [`agentmail inboxes drafts`](#agentmail-inboxes-drafts) - [`agentmail inboxes events`](#agentmail-inboxes-events) - [`agentmail inboxes lists`](#agentmail-inboxes-lists) @@ -81,6 +82,16 @@ agentmail agent verify --otp-code 123456 ### `agentmail api-keys` +#### `agentmail api-keys cancel-browser-enrollment` + +Cancel one pending, unexpired browser enrollment intent. Requires `api_key_delete`. + +`DELETE /v0/api-keys/browser-credentials/enrollments/{enrollment_id}` + +| Flag | Type | Required | Description | +|------|------|----------|-------------| +| `--enrollment-id` | `string (uuid)` | Yes | | + #### `agentmail api-keys create` **CLI:** @@ -120,6 +131,26 @@ agentmail api-keys delete --api-key-id |------|------|----------|-------------| | `--api-key-id` | `ApiKeyId` | Yes | | +#### `agentmail api-keys delete-browser-consent` + +Revoke one remembered AgentID client approval. Requires `api_key_delete`. + +`DELETE /v0/api-keys/browser-consents/{consent_id}` + +| Flag | Type | Required | Description | +|------|------|----------|-------------| +| `--consent-id` | `string` | Yes | | + +#### `agentmail api-keys delete-browser-credential` + +Permanently revoke one active browser credential. Requires `api_key_delete`. + +`DELETE /v0/api-keys/browser-credentials/{credential_id}` + +| Flag | Type | Required | Description | +|------|------|----------|-------------| +| `--credential-id` | `string (uuid)` | Yes | | + #### `agentmail api-keys list` **CLI:** @@ -135,6 +166,40 @@ agentmail api-keys list | `--page-token` | `PageToken` | No | | | `--ascending` | `Ascending` | No | | +#### `agentmail api-keys list-browser-consents` + +List remembered AgentID client approvals for one live inbox. Requires `api_key_read`. + +`GET /v0/api-keys/browser-consents` + +| Flag | Type | Required | Description | +|------|------|----------|-------------| +| `--inbox-id` | `string (email)` | Yes | | +| `--limit` | `BrowserAuthorizationListLimit` | No | | +| `--page-token` | `PageToken` | No | | + +#### `agentmail api-keys list-browser-credential-events` + +List owner-facing browser credential and consent lifecycle events. Requires `api_key_read`. + +`GET /v0/api-keys/browser-credentials/events` + +| Flag | Type | Required | Description | +|------|------|----------|-------------| +| `--limit` | `BrowserAuthorizationListLimit` | No | | +| `--page-token` | `PageToken` | No | | + +#### `agentmail api-keys list-browser-credentials` + +List active browser credentials visible to the caller's scope. Requires `api_key_read`. + +`GET /v0/api-keys/browser-credentials` + +| Flag | Type | Required | Description | +|------|------|----------|-------------| +| `--limit` | `BrowserAuthorizationListLimit` | No | | +| `--page-token` | `PageToken` | No | | + #### `agentmail api-keys list-public-keys` List only public-key credentials visible to the bearer caller's scope. @@ -474,6 +539,45 @@ agentmail inboxes api-keys list --inbox-id --- +### `agentmail inboxes browser-credentials` + +#### `agentmail inboxes browser-credentials create-enrollment` + +Attach a browser enrollment intent to the inbox. Requires +`api_key_create`. Before submitting `transaction_jti`, independently +verify that the browser page's final origin is exactly +`https://auth.agentid.com`. + +This endpoint is available to every organization using US production. +It is not available in EU production. + +Select `inbox_id` from trusted AgentMail configuration. An AgentID +`login_hint` is not authoritative for selecting the inbox; when the +transaction includes one, it must match the path inbox. + +**AgentMail API keys are sent only to `https://api.agentmail.to`; AgentID never requests them.** + +A new intent returns `202`; an idempotent retry for the same pending +transaction, inbox, and bearer key returns `200` with the same receipt. +An intent lasts at most five minutes. An activated credential lasts at +most 30 days and cannot outlive its authorizing bearer API key. + +Creation is limited to 20 intents per bearer API key per hour, 100 per +organization per hour, and five live unused intents per bearer API key. +Browser activation is separately limited to 20 activations per +authorizing bearer API key per UTC day. Either kind of limit can return +`429`; honor the `Retry-After` header. Cancelling an enrollment releases +its live-intent slot but does not reset the daily activation counter. + +`POST /v0/inboxes/{inbox_id}/browser-credentials/enrollments` + +| Flag | Type | Required | Description | +|------|------|----------|-------------| +| `--inbox-id` | `inboxesInboxId` | Yes | | +| `--json` | `JSON` | Yes | Request body as JSON (or use individual body-field flags) | + +--- + ### `agentmail inboxes drafts` #### `agentmail inboxes drafts create` diff --git a/src/app.rs b/src/app.rs index f7df330..2c7e93d 100644 --- a/src/app.rs +++ b/src/app.rs @@ -701,6 +701,10 @@ impl CliApp { let ctx = ErrorDisplayContext { docs_base_url: self.error_docs_base_url.clone(), help_hint: Some(help_hint), + // Resolved from raw argv because an error can precede clap parsing; + // the resolver is the same one the success path uses, so errors land + // in whichever representation the caller already asked for. + format: crate::formatter::resolve_format_from_raw_args(&str_args, &self.name), }; match self.dispatch_pipeline(args, out).await { Ok(PipelineOutcome::Success) => 0, @@ -750,7 +754,12 @@ impl CliApp { // binding to own the path wins. A real `Err` from one binding is // logged and the walk continues — one broken spec cannot mask its // sibling's surface. - if crate::cli_args::wants_schema(&str_args) { + // `--schema`, or `--help` with an explicit machine format — the spelling + // the generated README has always documented for the operation catalog. + let explicit_schema = crate::cli_args::wants_schema(&str_args); + let help_as_schema = crate::cli_args::wants_help(&str_args) + && crate::formatter::explicit_machine_format(&str_args); + if explicit_schema || help_as_schema { let path = crate::cli_args::extract_subcommand_path(&str_args); if path.is_empty() { @@ -840,10 +849,17 @@ impl CliApp { ), } } - return Err(CliError::Discovery(format!( - "--schema: no binding contains path `{}`", - path.join(" ") - ))); + // A path with no schema is an error for an explicit `--schema` — + // the caller asked for a document that does not exist. It must not + // be one for `--help`: built-in subcommands (`auth login`, + // `completion`, `man`) are not API operations, and someone asking + // for help on one should get help, not `discoveryError` exit 4. + if explicit_schema { + return Err(CliError::Discovery(format!( + "--schema: no binding contains path `{}`", + path.join(" ") + ))); + } } // 0c. --spec / --spec-raw: emit embedded OpenAPI spec(s) and exit. @@ -914,6 +930,19 @@ impl CliApp { .value_name("FORMAT") .global(true), ) + // The one shorthand a caller needs beyond TTY detection: piping + // already selects the machine rendering, so only the reverse — + // "I am a human, but I am paging or redirecting" — needs a flag. + // Deliberately not mirrored by a `--json`: endpoints with a request + // body already spell that flag `--json `. + .arg( + clap::Arg::new("human") + .long("human") + .help("Force human-readable output (same as --format table) even when piped") + .action(clap::ArgAction::SetTrue) + .conflicts_with("format") + .global(true), + ) .arg( clap::Arg::new("base-url") .long("base-url") @@ -1212,6 +1241,7 @@ fn graft_merged_subtree( // avoid clap panic on duplicates). let mut seen_arg_ids: std::collections::HashSet = [ "format".to_string(), + "human".to_string(), "base-url".to_string(), "user-agent-suffix".to_string(), "schema".to_string(), @@ -1269,8 +1299,22 @@ fn graft_builtin_command(cli: clap::Command, builtin: clap::Command) -> clap::Co // are intercepted pre-clap anyway, so the built-in wins. return cli.mut_subcommand(name, move |_spec_owned| builtin); } + let builtin_about = builtin.get_about().map(ToString::to_string); + let builtin_long_about = builtin.get_long_about().map(ToString::to_string); + let generic_about = crate::openapi::commands::generic_group_about(&name); cli.mut_subcommand(name, move |spec_owned| { let mut merged = spec_owned; + let spec_uses_generic_about = merged + .get_about() + .is_none_or(|about| about.to_string() == generic_about); + if spec_uses_generic_about { + if let Some(about) = builtin_about { + merged = merged.about(about); + } + if let Some(long_about) = builtin_long_about { + merged = merged.long_about(long_about); + } + } for sub in builtin_subs { merged = crate::custom_commands::graft_subcommand(merged, &[], sub); } @@ -1552,6 +1596,26 @@ fn global_flags() -> Vec { "flag": "--spec-raw", "description": "Print the byte-exact embedded source OpenAPI spec(s) to stdout", }), + // Registered on every operation command, so genuinely global from an + // agent's point of view even though they are not clap `global(true)` + // args. Their absence meant an agent reading `--schema` did not know + // `--params` or `--no-retry` existed at all. + serde_json::json!({ + "flag": "--params", + "description": "Supply parameters as a JSON object instead of individual flags; overrides them where both are given", + }), + serde_json::json!({ + "flag": "--no-retry", + "description": "Disable retries declared by x-fern-retries on this operation, including network errors", + }), + serde_json::json!({ + "flag": "--no-extract", + "description": "Disable x-fern-sdk-return-value extraction and print the full response body", + }), + serde_json::json!({ + "flag": "--help", + "description": "Print help for this scope (human-readable counterpart to --schema)", + }), ] } @@ -1770,13 +1834,49 @@ mod tests { assert!(login.get_arguments().any(|a| a.get_id() == "with-token")); } + #[test] + fn graft_builtin_about_wins_over_generic_spec_fallback() { + let spec = clap::Command::new("root").subcommand( + clap::Command::new("auth") + .about(crate::openapi::commands::generic_group_about("auth")) + .subcommand(clap::Command::new("me")), + ); + let cli = graft_builtin_command(spec, crate::auth::login::build_auth_command()); + let auth = cli.find_subcommand("auth").expect("auth group survives"); + assert_eq!( + auth.get_about().map(ToString::to_string).as_deref(), + Some("Manage credentials (login / logout / status)"), + ); + } + + #[test] + fn graft_builtin_preserves_real_spec_about() { + let spec = clap::Command::new("root").subcommand( + clap::Command::new("auth") + .about("API authentication operations") + .subcommand(clap::Command::new("me")), + ); + let cli = graft_builtin_command(spec, crate::auth::login::build_auth_command()); + let auth = cli.find_subcommand("auth").expect("auth group survives"); + assert_eq!( + auth.get_about().map(ToString::to_string).as_deref(), + Some("API authentication operations"), + ); + } + #[test] fn graft_builtin_registers_when_no_collision() { let cli = graft_builtin_command( clap::Command::new("root").subcommand(clap::Command::new("users")), crate::auth::login::build_auth_command(), ); - assert!(cli.find_subcommand("auth").is_some()); + assert_eq!( + cli.find_subcommand("auth") + .and_then(|auth| auth.get_about()) + .map(ToString::to_string) + .as_deref(), + Some("Manage credentials (login / logout / status)"), + ); assert!(cli.find_subcommand("users").is_some()); } diff --git a/src/auth/compose.rs b/src/auth/compose.rs index 3d5c963..b0ace5d 100644 --- a/src/auth/compose.rs +++ b/src/auth/compose.rs @@ -54,6 +54,13 @@ impl AuthProvider for AnyAuthProvider { .collect() } + fn populated_credential_hints(&self) -> Vec { + self.providers + .iter() + .flat_map(|p| p.populated_credential_hints()) + .collect() + } + fn has_credentials_for(&self, endpoint: &EndpointAuthMetadata) -> bool { self.providers .iter() @@ -131,6 +138,13 @@ impl AuthProvider for AllAuthProvider { .collect() } + fn populated_credential_hints(&self) -> Vec { + self.providers + .iter() + .flat_map(|p| p.populated_credential_hints()) + .collect() + } + fn has_credentials_for(&self, endpoint: &EndpointAuthMetadata) -> bool { !self.providers.is_empty() && self @@ -220,6 +234,13 @@ impl AuthProvider for LayeredAuthProvider { self.primary.credential_hints() } + fn populated_credential_hints(&self) -> Vec { + // Same primary-only rule; without this override the composite fell + // back to the trait default, which returns the *unfiltered* hints and + // silently undid the filtering for every CLI wrapped in a layer. + self.primary.populated_credential_hints() + } + fn has_credentials_for(&self, endpoint: &EndpointAuthMetadata) -> bool { self.primary.has_credentials_for(endpoint) } @@ -320,6 +341,25 @@ impl AuthProvider for RoutingAuthProvider { hints } + /// Same walk as [`credential_hints`](Self::credential_hints), filtered. + /// + /// Missing this override made the populated-hints filtering inert on the + /// most common configuration there is: a `RoutingAuthProvider` is built + /// whenever any operation declares per-operation `security:`, which on a + /// real spec means nearly every operation. The trait default returns the + /// unfiltered hints, so a 401 still named every declared source. + fn populated_credential_hints(&self) -> Vec { + let mut hints: Vec = self + .schemes + .values() + .flat_map(|p| p.populated_credential_hints()) + .collect(); + if let Some(d) = &self.default { + hints.extend(d.populated_credential_hints()); + } + hints + } + /// Endpoint-aware credential check. /// /// - **No requirements declared**: defer to the wrapper's `default` diff --git a/src/auth/credential.rs b/src/auth/credential.rs index e041e12..fffa1a4 100644 --- a/src/auth/credential.rs +++ b/src/auth/credential.rs @@ -25,6 +25,8 @@ use std::sync::Arc; use secrecy::SecretString; +use crate::error::CliError; + use crate::auth::keyring_store::active_store; type CredentialClosure = Arc Option + Send + Sync>; @@ -133,8 +135,26 @@ impl AuthCredentialSource { /// `Debug`/`Display`/panic messages. Callers that need the raw `&str` /// (to build a `HeaderValue`, base64-encode for basic auth, etc.) /// must opt in explicitly via [`ExposeSecret::expose_secret`]. + /// + /// Treats every failure as "no credential". Correct for *probing* — an + /// `auth status` listing or a `has_credentials` check should report what is + /// usable, not abort — but wrong on the request path, where a credential + /// the user has but the CLI could not read must not silently downgrade the + /// request to unauthenticated. Use [`try_resolve`](Self::try_resolve) there. pub fn resolve(&self) -> Option { - match self { + self.try_resolve().ok().flatten() + } + + /// Resolve the value, distinguishing "not configured" (`Ok(None)`) from + /// "configured but unreadable" (`Err`). + /// + /// The distinction only exists for the keyring today, and it is the one + /// that matters: denying the OS keychain prompt used to be indistinguishable + /// from having stored nothing, so the CLI sent the request anonymously and + /// the user saw whatever the server says to a stranger — a 404, typically — + /// instead of an auth failure. + pub fn try_resolve(&self) -> Result, CliError> { + let value = match self { AuthCredentialSource::Env(name) => std::env::var(name) .ok() .map(|v| v.trim().to_string()) @@ -144,16 +164,29 @@ impl AuthCredentialSource { AuthCredentialSource::File(path) => read_credential_file(path), AuthCredentialSource::Literal(v) if v.is_empty() => None, AuthCredentialSource::Literal(v) => Some(SecretString::from(v.clone())), - AuthCredentialSource::Chain(sources) => sources.iter().find_map(|s| s.resolve()), - AuthCredentialSource::Closure(f, _) => f().filter(|v| !v.is_empty()).map(SecretString::from), + // First source that yields a value wins; a source that *errors* + // stops the walk rather than falling through, so a denied keychain + // is reported instead of being masked by a later empty rung. + AuthCredentialSource::Chain(sources) => { + let mut found = None; + for source in sources { + if let Some(v) = source.try_resolve()? { + found = Some(v); + break; + } + } + found + } + AuthCredentialSource::Closure(f, _) => { + f().filter(|v| !v.is_empty()).map(SecretString::from) + } AuthCredentialSource::Keyring { service, account } => active_store() - .get(service, account) - .ok() - .flatten() + .get(service, account)? .filter(|v| !v.is_empty()) .map(SecretString::from), AuthCredentialSource::Missing => None, - } + }; + Ok(value) } /// The environment-variable name backing this source, if it is an @@ -184,7 +217,15 @@ impl AuthCredentialSource { } AuthCredentialSource::Closure(_, Some(hint)) => vec![hint.clone()], AuthCredentialSource::Keyring { service, account } => { - vec![format!("keyring entry {service}:{account} (populated by ` auth login`)")] + // `service` is the CLI's binary name (see the variant's docs), + // so the command is spellable. It used to render as a literal + // ``, which reached users on any non-OAuth scheme — the + // OAuth provider overrides `credential_hints` and substituted + // correctly, hiding the placeholder on the one path anyone + // looked at. + vec![format!( + "keyring entry {service}:{account} (populated by `{service} auth login`)" + )] } AuthCredentialSource::Literal(_) | AuthCredentialSource::Closure(_, None) @@ -192,6 +233,42 @@ impl AuthCredentialSource { } } + /// Like [`credential_hints`](Self::credential_hints), but only the sources + /// that currently hold a value. + /// + /// The 401/403 path used to list every *declared* source — so a CLI with + /// two schemes told the user "Credentials were supplied via: TOKEN env var, + /// keyring …, API_KEY env var, keyring …" when exactly one of them had + /// supplied anything, and then advised hunting for shadowing that did not + /// exist. Naming only what actually resolved makes the message true, and + /// lets the caller decide whether shadowing is even possible. + /// + /// Resolution cost is paid only on the error path. `Cli` sources always + /// report empty (they resolve post-finalize, so `try_resolve` cannot see + /// them) and `Closure` sources are reported without invoking the closure — + /// re-running it here could have side effects, and it has already run + /// during `apply()`. + pub fn populated_credential_hints(&self) -> Vec { + match self { + AuthCredentialSource::Chain(sources) => sources + .iter() + .flat_map(|s| s.populated_credential_hints()) + .collect(), + // Can't be checked here without side effects or post-finalize + // state; keep them so the user still sees a source they can use. + AuthCredentialSource::Cli(_) | AuthCredentialSource::Closure(_, Some(_)) => { + self.credential_hints() + } + // `try_resolve` rather than `resolve`: a keyring entry that exists + // but could not be read (denied prompt) is *configured*, and saying + // otherwise would send the user looking in the wrong place. + _ => match self.try_resolve() { + Ok(Some(_)) | Err(_) => self.credential_hints(), + Ok(None) => Vec::new(), + }, + } + } + /// Recursively collect every CLI arg name this source references. /// CliApp uses this before clap parsing to register the corresponding /// global `--` flags. @@ -711,6 +788,59 @@ mod tests { assert_eq!(resolved(&s), None); } + /// A store that fails every read, standing in for a denied OS keychain + /// prompt (`keyring::Error::PlatformFailure` on macOS). + #[derive(Debug)] + struct DeniedKeyringStore; + + impl crate::auth::keyring_store::KeyringStore for DeniedKeyringStore { + fn get(&self, _: &str, _: &str) -> Result, CliError> { + Err(CliError::Auth("keyring get failed: User denied access".into())) + } + fn set(&self, _: &str, _: &str, _: &str) -> Result<(), CliError> { + unreachable!() + } + fn delete(&self, _: &str, _: &str) -> Result<(), CliError> { + unreachable!() + } + fn backend_label(&self) -> String { + "denied".into() + } + } + + #[test] + #[serial_test::serial] + fn a_denied_keyring_is_an_error_not_an_absent_credential() { + use crate::auth::keyring_store::set_active_store; + set_active_store(Arc::new(DeniedKeyringStore)); + + let s = AuthCredentialSource::keyring("svc", "OAuth2"); + // The request path must be able to tell these apart: sending the + // request anonymously makes the server answer as if to a stranger, and + // the user sees a 404 instead of an auth failure. + let err = s.try_resolve().unwrap_err(); + assert!(matches!(err, CliError::Auth(_)), "got: {err:?}"); + assert_eq!(err.exit_code(), CliError::EXIT_CODE_AUTH); + // The probing path still degrades to "no credential", so `auth status` + // and `has_credentials` keep working rather than aborting. + assert!(s.resolve().is_none()); + } + + #[test] + #[serial_test::serial] + fn a_chain_stops_at_a_denied_keyring_rather_than_falling_through() { + use crate::auth::keyring_store::set_active_store; + set_active_store(Arc::new(DeniedKeyringStore)); + + // Env is empty, so without propagation the chain would report "no + // credential anywhere" and the denial would vanish. + let chain = AuthCredentialSource::any([ + AuthCredentialSource::keyring("svc", "OAuth2"), + AuthCredentialSource::from_env("__FERN_TEST_DEFINITELY_UNSET"), + ]); + assert!(matches!(chain.try_resolve(), Err(CliError::Auth(_)))); + } + #[test] #[serial_test::serial] fn keyring_source_empty_value_resolves_to_none() { diff --git a/src/auth/error.rs b/src/auth/error.rs index 19c7554..1d7eb00 100644 --- a/src/auth/error.rs +++ b/src/auth/error.rs @@ -13,8 +13,6 @@ //! //! [hcf]: crate::auth::AuthProvider::has_credentials_for -use serde_json::Value; - use crate::auth::provider::{AuthProvider, EndpointAuthMetadata}; use crate::error::CliError; @@ -55,33 +53,104 @@ pub fn handle_error_response( // Surface which source supplied the credential so the user can // diagnose shadowing (e.g. stale env var winning over a fresh // `auth login`). ADR-0008 § 8e. - let hints = dedup_preserve_order(provider.credential_hints()); - if !hints.is_empty() { + // Only the sources that actually supplied something. Listing every + // declared source named places the user had never configured. + let supplied = dedup_preserve_order(provider.populated_credential_hints()); + if !supplied.is_empty() { let base = parse_api_error(status, error_body); - return Err(decorate_with_source_hint(base, &hints)); + return Err(decorate_with_source_hint(base, &supplied)); + } + return Err(parse_api_error(status, error_body)); + } + + // Any other failure, sent without credentials. The condition the friendly + // message above describes — "this request carried no auth" — has nothing to + // do with the status code, but the message was gated on 401/403, so an API + // that expresses "not authenticated" some other way said nothing useful. + // + // ElevenLabs answers 404 for a workspace scoped to the anonymous principal + // (a deliberate pattern: a 401 would confirm the resource exists), so a + // logged-out user saw `Workspace 1anonymous1 not found` with no hint that + // the fix was `auth login`. + // + // This only *annotates* — the error keeps its class and exit code, because + // the server, not the spec, is the authority on whether this endpoint + // needed auth at all. Public endpoints must keep working. + if !provider.has_credentials_for(endpoint) { + let base = parse_api_error(status, error_body); + if let Some(note) = missing_credentials_note(provider) { + return Err(decorate_with_help(base, note)); } + return Err(base); } Err(parse_api_error(status, error_body)) } -/// Append a "Credentials were supplied via: …" line to an existing -/// `CliError::Api` message, preserving the structured fields. For -/// non-`Api` variants (defensive — shouldn't happen here), pass through. -fn decorate_with_source_hint(err: CliError, hints: &[String]) -> CliError { - let joined = hints.join(", "); +/// Advice for a request that went out with no credentials at all, or `None` +/// when the CLI declares no auth sources to point at. +fn missing_credentials_note(provider: &dyn AuthProvider) -> Option { + let hints = dedup_preserve_order(provider.credential_hints()); + if hints.is_empty() { + return None; + } + Some(format!( + "No credentials were sent with this request. Set {}.", + hints.join(", ") + )) +} + +/// Attach `note` as the error's `help`, leaving every other field alone. +fn decorate_with_help(err: CliError, note: String) -> CliError { match err { - CliError::Api { code, message, reason } => CliError::Api { + CliError::Api { + code, + message, + reason, + details, + .. + } => CliError::Api { code, - message: format!( - "{message}\nCredentials were supplied via: {joined}. \ - Run `auth status` to see all visible sources and check for shadowing." - ), + message, reason, + details, + help: Some(note), }, other => other, } } +/// Attach a "Credentials were supplied via: …" note to an existing +/// `CliError::Api`. The note goes in `help` rather than `message` — it is +/// advice about the failure, not part of what the server reported. +/// +/// Naming the source that was used is worth saying whenever credentials were +/// sent. The follow-up — "check for shadowing" — is not: shadowing means one +/// source silently outranking another, which cannot happen when there is only +/// one. On a single-source CLI it sent the reader to `auth status` to compare a +/// list of one against itself, which is busywork at best and, on a scope +/// failure like `OAuth token does not have required permissions`, actively +/// points away from the real fix. +/// Note for a request that carried credentials the server rejected. +/// +/// `hints` must be the *populated* sources, not every declared one. The +/// shadowing advice is gated on more than one source actually holding a value, +/// because that is the only situation in which shadowing is possible — gating +/// it on the declared count meant any CLI with two auth schemes always told +/// the user to go looking for a conflict that could not exist. +fn decorate_with_source_hint(err: CliError, hints: &[String]) -> CliError { + let joined = hints.join(", "); + let note = if hints.len() > 1 { + format!( + "Credentials were supplied via: {joined}. More than one source has a \ + value, so one may be shadowing another — run `auth status` to see \ + which was used." + ) + } else { + format!("Credentials were supplied via: {joined}.") + }; + decorate_with_help(err, note) +} + /// Deduplicate strings while preserving first-seen order. fn dedup_preserve_order(items: Vec) -> Vec { let mut seen = std::collections::HashSet::new(); @@ -94,38 +163,7 @@ fn dedup_preserve_order(items: Vec) -> Vec { /// Shared parsing for the auth-aware error handler. Returns a structured /// [`CliError::Api`] whether or not the body was JSON. fn parse_api_error(status: reqwest::StatusCode, error_body: &str) -> CliError { - if let Ok(error_json) = serde_json::from_str::(error_body) { - if let Some(err_obj) = error_json.get("error") { - let code = err_obj - .get("code") - .and_then(|c| c.as_u64()) - .unwrap_or(status.as_u16() as u64) as u16; - let message = err_obj - .get("message") - .and_then(|m| m.as_str()) - .unwrap_or("Unknown error") - .to_string(); - let reason = err_obj - .get("errors") - .and_then(|e| e.as_array()) - .and_then(|arr| arr.first()) - .and_then(|e| e.get("reason")) - .and_then(|r| r.as_str()) - .or_else(|| err_obj.get("reason").and_then(|r| r.as_str())) - .unwrap_or("unknown") - .to_string(); - return CliError::Api { - code, - message, - reason, - }; - } - } - CliError::Api { - code: status.as_u16(), - message: error_body.to_string(), - reason: "httpError".to_string(), - } + crate::error::api_error_from_body(status.as_u16(), error_body) } #[cfg(test)] @@ -183,7 +221,12 @@ mod tests { ) .unwrap_err(); match err { - CliError::Api { code, message, reason } => { + CliError::Api { + code, + message, + reason, + .. + } => { assert_eq!(code, 401); assert!(message.contains("invalid authentication credentials")); assert_eq!(reason, "authError"); @@ -223,10 +266,15 @@ mod tests { ) .unwrap_err(); match err { - CliError::Api { code, message, reason } => { + CliError::Api { + code, + message, + reason, + .. + } => { assert_eq!(code, 500); assert_eq!(message, "Internal Server Error Text"); - assert_eq!(reason, "httpError"); + assert_eq!(reason, "internalServerError"); } _ => panic!("Expected Api"), } @@ -426,15 +474,96 @@ mod tests { ) .unwrap_err(); match err { - CliError::Api { message, .. } => { - assert!(message.contains("__FERN_TEST_SHADOW_TOKEN")); - assert!(message.contains("auth status")); + CliError::Api { message, help, .. } => { + // The hint is advice, so it lives in `help` — `message` stays + // exactly what the server said. + assert_eq!(message, "bad token"); + let help = help.expect("credential source hint"); + assert!(help.contains("__FERN_TEST_SHADOW_TOKEN")); + // One source, so nothing can be shadowing anything: the + // `auth status` follow-up would send the reader to compare a + // list of one against itself. + assert!(!help.contains("auth status"), "got: {help}"); } other => panic!("expected Api with source-hint suffix, got: {other:?}"), } std::env::remove_var("__FERN_TEST_SHADOW_TOKEN"); } + #[test] + fn names_only_the_sources_that_actually_hold_a_value() { + // A declared-but-empty source must not be named. Listing every source + // the provider *could* read told users their credential came from a + // keyring entry they had never populated, and — because the shadowing + // advice was gated on the declared count — sent them hunting for a + // conflict that could not exist. + std::env::set_var("__FERN_TEST_CHAIN_A", "stale"); + let p = BearerAuthProvider::new( + "bearer", + AuthCredentialSource::any([ + AuthCredentialSource::from_env("__FERN_TEST_CHAIN_A"), + AuthCredentialSource::from_env("__FERN_TEST_CHAIN_UNSET"), + ]), + ); + let err = handle_error_response::<()>( + reqwest::StatusCode::UNAUTHORIZED, + r#"{"error":{"code":401,"message":"bad token"}}"#, + &p, + &EndpointAuthMetadata::unspecified(), + ) + .unwrap_err(); + match err { + CliError::Api { help, .. } => { + let help = help.expect("credential source hint"); + assert!(help.contains("__FERN_TEST_CHAIN_A"), "got: {help}"); + assert!( + !help.contains("__FERN_TEST_CHAIN_UNSET"), + "an unset source must not be named: {help}", + ); + assert!( + !help.contains("shadowing"), + "one populated source cannot be shadowed: {help}", + ); + } + other => panic!("expected Api, got: {other:?}"), + } + std::env::remove_var("__FERN_TEST_CHAIN_A"); + } + + #[test] + fn shadowing_advice_appears_only_with_more_than_one_source() { + // Two *populated* sources is the situation the advice was written for: + // a stale env var can outrank a fresh one, and which won is not + // observable from the error alone. + std::env::set_var("__FERN_TEST_SHADOW_A", "stale"); + std::env::set_var("__FERN_TEST_SHADOW_B", "fresh"); + let p = BearerAuthProvider::new( + "bearer", + AuthCredentialSource::any([ + AuthCredentialSource::from_env("__FERN_TEST_SHADOW_A"), + AuthCredentialSource::from_env("__FERN_TEST_SHADOW_B"), + ]), + ); + let err = handle_error_response::<()>( + reqwest::StatusCode::UNAUTHORIZED, + r#"{"error":{"code":401,"message":"bad token"}}"#, + &p, + &EndpointAuthMetadata::unspecified(), + ) + .unwrap_err(); + match err { + CliError::Api { help, .. } => { + let help = help.expect("credential source hint"); + assert!(help.contains("__FERN_TEST_SHADOW_A"), "got: {help}"); + assert!(help.contains("__FERN_TEST_SHADOW_B"), "got: {help}"); + assert!(help.contains("shadowing"), "got: {help}"); + } + other => panic!("expected Api, got: {other:?}"), + } + std::env::remove_var("__FERN_TEST_SHADOW_A"); + std::env::remove_var("__FERN_TEST_SHADOW_B"); + } + #[test] fn forbidden_with_credentials_discloses_source() { std::env::set_var("__FERN_TEST_FORBIDDEN_TOKEN", "x"); @@ -450,8 +579,10 @@ mod tests { ) .unwrap_err(); match err { - CliError::Api { message, .. } => { - assert!(message.contains("__FERN_TEST_FORBIDDEN_TOKEN")); + CliError::Api { help, .. } => { + assert!(help + .expect("credential source hint") + .contains("__FERN_TEST_FORBIDDEN_TOKEN")); } other => panic!("expected Api with source-hint suffix, got: {other:?}"), } @@ -473,14 +604,81 @@ mod tests { ) .unwrap_err(); match err { - CliError::Api { message, .. } => { + CliError::Api { message, help, .. } => { assert!(!message.contains("__FERN_TEST_NONAUTH_TOKEN")); + assert!(help.is_none()); } _ => panic!("expected Api"), } std::env::remove_var("__FERN_TEST_NONAUTH_TOKEN"); } + #[test] + fn a_credentialless_request_is_annotated_whatever_the_status() { + // ElevenLabs answers 404 for a workspace scoped to the anonymous + // principal, so a logged-out user used to read `Workspace 1anonymous1 + // not found` with nothing pointing at `auth login`. The hint was gated + // on 401/403; the condition it describes is status-independent. + std::env::remove_var("__FERN_TEST_ABSENT_TOKEN"); + let p = BearerAuthProvider::new( + "bearer", + AuthCredentialSource::from_env("__FERN_TEST_ABSENT_TOKEN"), + ); + let err = handle_error_response::<()>( + reqwest::StatusCode::NOT_FOUND, + r#"{"detail":{"message":"Workspace 1anonymous1 not found.","code":"workspace_not_found"}}"#, + &p, + &EndpointAuthMetadata::unspecified(), + ) + .unwrap_err(); + match err { + CliError::Api { + code, + message, + reason, + help, + .. + } => { + // Class and exit code are untouched: the server, not the spec, + // decides whether this endpoint needed auth. + assert_eq!(code, 404); + assert_eq!(reason, "workspace_not_found"); + assert_eq!(message, "Workspace 1anonymous1 not found."); + let help = help.expect("missing-credentials note"); + assert!(help.contains("No credentials were sent"), "got: {help}"); + assert!(help.contains("__FERN_TEST_ABSENT_TOKEN"), "got: {help}"); + } + other => panic!("expected Api, got: {other:?}"), + } + assert_eq!( + CliError::api(404, "x", "y").exit_code(), + CliError::EXIT_CODE_API + ); + } + + #[test] + fn a_credentialled_request_gets_no_missing_credentials_note() { + // The note must not fire just because a call failed — only when the + // request actually went out with nothing. + std::env::set_var("__FERN_TEST_PRESENT_TOKEN", "x"); + let p = BearerAuthProvider::new( + "bearer", + AuthCredentialSource::from_env("__FERN_TEST_PRESENT_TOKEN"), + ); + let err = handle_error_response::<()>( + reqwest::StatusCode::NOT_FOUND, + r#"{"detail":"Nope"}"#, + &p, + &EndpointAuthMetadata::unspecified(), + ) + .unwrap_err(); + match err { + CliError::Api { help, .. } => assert!(help.is_none(), "got: {help:?}"), + other => panic!("expected Api, got: {other:?}"), + } + std::env::remove_var("__FERN_TEST_PRESENT_TOKEN"); + } + #[test] fn dedup_removes_duplicates_preserving_order() { let input = vec!["a".into(), "b".into(), "a".into(), "c".into(), "b".into()]; diff --git a/src/auth/keyring_store.rs b/src/auth/keyring_store.rs index e488caf..205647a 100644 --- a/src/auth/keyring_store.rs +++ b/src/auth/keyring_store.rs @@ -18,6 +18,20 @@ //! [`AuthCredentialSource::Keyring`](crate::auth::AuthCredentialSource) //! reads through it at resolve time. //! +//! The OS backend is wrapped in [`ChunkedKeyringStore`], which splits +//! oversized values across multiple entries. Windows Credential Manager +//! caps a credential blob at `CRED_MAX_CREDENTIAL_BLOB_SIZE` (2560 bytes, +//! i.e. 1280 UTF-16 code units), which an OAuth token bundle for an API +//! with many scopes easily exceeds. VS Code and the Azure CLI chunk the +//! same way for the same reason. +//! +//! Only Windows *writes* chunks. macOS Keychain and secret-service have no +//! comparable limit, so chunking there would change the on-disk shape of a +//! credential that already worked — and an older binary, which knows +//! nothing of markers, would read the marker back and send it as the +//! token. Reads stay marker-aware on every platform, so a chunked entry is +//! always reassembled correctly wherever it is encountered. +//! //! ## Entry shape //! //! Keyed by `(service=, account=)`. The value is an @@ -208,6 +222,223 @@ impl KeyringStore for FileKeyringStore { // `atomic_write`, `home_dir`, and `config_dir` are shared with the // existing `TokenCache` (oauth2.rs); see [`crate::auth::oauth_common`]. +// --------------------------------------------------------------------------- +// Chunking wrapper (Windows Credential Manager blob-size limit) +// --------------------------------------------------------------------------- + +/// Prefix of the marker written to the primary entry when a value is +/// split across multiple keyring entries. The full marker is +/// `:`. +/// Deliberately not valid JSON and not a plausible token prefix, so a +/// plain value stored by an older binary can never be misread as chunked. +const CHUNK_MARKER_PREFIX: &str = "__fern-cli-chunked-v1__:"; + +/// Maximum UTF-16 code units stored in a single entry. Windows Credential +/// Manager rejects blobs over `CRED_MAX_CREDENTIAL_BLOB_SIZE` (2560 bytes +/// = 1280 UTF-16 code units, enforced by the `keyring` crate); stay under +/// it with margin. macOS Keychain and secret-service have no such limit, +/// so chunking there is harmless. +const MAX_ENTRY_UTF16_UNITS: usize = 1024; + +/// Contents of a chunk marker: how many chunks the value was split into +/// and the total UTF-16 length of the reassembled value. +#[derive(Debug, Clone, Copy, PartialEq, Eq)] +struct ChunkMarker { + count: usize, + utf16_len: usize, +} + +/// Wraps a [`KeyringStore`] and transparently splits values that exceed +/// [`MAX_ENTRY_UTF16_UNITS`] across sibling entries. +/// +/// Layout for a value split into `n` chunks under `(service, account)`: +/// - `(service, account)` → `__fern-cli-chunked-v1__::` +/// - `(service, account#1..=n)` → the chunks, in order +/// +/// The marker carries the reassembled value's UTF-16 length so that a +/// torn write — the process dying between chunk writes, leaving a stale +/// marker describing a mix of old and new chunks — is detected on read +/// instead of yielding a corrupt value. See [`ChunkedKeyringStore::get`]. +/// +/// Values that fit in one entry are stored as-is, so existing plain +/// credentials written by older binaries read back unchanged. +/// +/// Writing chunks is opt-in per platform (see +/// [`for_platform`](Self::for_platform)); reading them never is. +#[derive(Debug)] +pub struct ChunkedKeyringStore { + inner: S, + /// Whether oversized values are split on write. Reads reassemble + /// chunked entries regardless of this flag. + chunk_writes: bool, +} + +impl ChunkedKeyringStore { + /// Wrap `inner`, splitting oversized values on write. + pub fn new(inner: S) -> Self { + Self::with_chunk_writes(inner, true) + } + + /// Wrap `inner`, splitting on write only where the platform requires + /// it — today, Windows alone. Elsewhere values are written plain, byte + /// for byte as a pre-chunking binary would, so downgrading the CLI + /// cannot strand a credential behind a marker the old build can't read. + /// Reads stay marker-aware either way. + pub fn for_platform(inner: S) -> Self { + Self::with_chunk_writes(inner, cfg!(target_os = "windows")) + } + + fn with_chunk_writes(inner: S, chunk_writes: bool) -> Self { + Self { + inner, + chunk_writes, + } + } + + fn chunk_account(account: &str, index: usize) -> String { + format!("{account}#{index}") + } + + /// Parse a primary entry as a chunk marker. `None` means the value is + /// a plain (unchunked) credential and should be returned as-is. + fn parse_marker(value: &str) -> Option { + let (count, utf16_len) = value.strip_prefix(CHUNK_MARKER_PREFIX)?.split_once(':')?; + Some(ChunkMarker { + count: count.parse().ok()?, + utf16_len: utf16_len.parse().ok()?, + }) + } + + fn format_marker(marker: ChunkMarker) -> String { + format!("{CHUNK_MARKER_PREFIX}{}:{}", marker.count, marker.utf16_len) + } + + /// Chunk count recorded in the primary entry, or 0 if it is absent or + /// holds a plain value. Used to reap chunks the new value doesn't use. + fn stored_chunk_count(&self, service: &str, account: &str) -> Result { + Ok(self + .inner + .get(service, account)? + .as_deref() + .and_then(Self::parse_marker) + .map(|marker| marker.count) + .unwrap_or(0)) + } + + /// Split on char boundaries so no chunk exceeds the per-entry UTF-16 + /// budget (surrogate pairs are never divided). + fn split_chunks(value: &str) -> Vec { + let mut chunks = Vec::new(); + let mut current = String::new(); + let mut units = 0usize; + for ch in value.chars() { + let n = ch.len_utf16(); + if units + n > MAX_ENTRY_UTF16_UNITS && !current.is_empty() { + chunks.push(std::mem::take(&mut current)); + units = 0; + } + current.push(ch); + units += n; + } + if !current.is_empty() || chunks.is_empty() { + chunks.push(current); + } + chunks + } +} + +impl KeyringStore for ChunkedKeyringStore { + fn get(&self, service: &str, account: &str) -> Result, CliError> { + let Some(primary) = self.inner.get(service, account)? else { + return Ok(None); + }; + let Some(marker) = Self::parse_marker(&primary) else { + return Ok(Some(primary)); + }; + let ChunkMarker { count, utf16_len } = marker; + let mut value = String::new(); + for i in 1..=count { + match self.inner.get(service, &Self::chunk_account(account, i))? { + Some(chunk) => value.push_str(&chunk), + // A missing chunk means the entry set was partially removed + // (e.g. by hand in a credential manager UI). Treat the whole + // credential as absent so the caller prompts a fresh login + // rather than resolving a truncated token. + None => { + tracing::warn!( + "keyring entry {service}/{account} is chunked but chunk {i}/{count} is missing; treating credential as absent" + ); + return Ok(None); + } + } + } + // A length mismatch means the chunk set does not match the marker + // that describes it — the tell-tale of a write torn partway through + // (process killed between chunk writes), which leaves a stale marker + // pointing at a mix of old and new chunks. Reassembling that would + // hand back a corrupt token, so treat it as absent and re-login. + let actual_utf16_len = value.encode_utf16().count(); + if actual_utf16_len != utf16_len { + tracing::warn!( + "keyring entry {service}/{account} reassembled to {actual_utf16_len} UTF-16 units but its marker records {utf16_len}; treating credential as absent" + ); + return Ok(None); + } + Ok(Some(value)) + } + + fn set(&self, service: &str, account: &str, value: &str) -> Result<(), CliError> { + let old_count = self.stored_chunk_count(service, account)?; + + let chunks = if self.chunk_writes { + Self::split_chunks(value) + } else { + // No blob-size limit on this platform: store plain at any size. + // Stale chunks from an entry written by a chunking build are + // still reaped below. + vec![value.to_string()] + }; + let new_count = if chunks.len() > 1 { chunks.len() } else { 0 }; + if new_count == 0 { + self.inner.set(service, account, value)?; + } else { + // Chunks first, marker last: a failure mid-write leaves the + // previous primary entry (plain value or old marker) intact. + // The old marker then describes a chunk set it no longer + // matches, which `get` catches via the recorded UTF-16 length. + for (i, chunk) in chunks.iter().enumerate() { + self.inner + .set(service, &Self::chunk_account(account, i + 1), chunk)?; + } + let marker = ChunkMarker { + count: chunks.len(), + utf16_len: value.encode_utf16().count(), + }; + self.inner + .set(service, account, &Self::format_marker(marker))?; + } + + // Remove chunks left over from a previous, larger value. + for i in (new_count + 1)..=old_count { + self.inner.delete(service, &Self::chunk_account(account, i))?; + } + Ok(()) + } + + fn delete(&self, service: &str, account: &str) -> Result<(), CliError> { + let old_count = self.stored_chunk_count(service, account)?; + self.inner.delete(service, account)?; + for i in 1..=old_count { + self.inner.delete(service, &Self::chunk_account(account, i))?; + } + Ok(()) + } + + fn backend_label(&self) -> String { + self.inner.backend_label() + } +} + // --------------------------------------------------------------------------- // Auto-pick + process-global handle // --------------------------------------------------------------------------- @@ -233,7 +464,7 @@ pub fn auto_store() -> Arc { { if OsKeyringStore::probe().is_ok() { tracing::debug!("Using OS keyring backend for credential storage"); - return Arc::new(OsKeyringStore); + return Arc::new(ChunkedKeyringStore::for_platform(OsKeyringStore)); } } tracing::debug!("OS keyring unavailable; falling back to file backend"); @@ -447,6 +678,215 @@ mod tests { assert_eq!(mock1.get("svc", "acct").unwrap().as_deref(), Some("v1")); } + fn value_of_utf16_units(units: usize) -> String { + "x".repeat(units) + } + + #[test] + fn chunked_store_small_value_stored_plain() { + let mock = MockKeyringStore::new(); + let store = ChunkedKeyringStore::new(mock.clone()); + + store.set("svc", "acct", "short-token").unwrap(); + assert_eq!(mock.get("svc", "acct").unwrap().as_deref(), Some("short-token")); + assert_eq!(store.get("svc", "acct").unwrap().as_deref(), Some("short-token")); + assert_eq!(mock.snapshot().len(), 1); + } + + #[test] + fn chunked_store_splits_and_reassembles_large_value() { + let mock = MockKeyringStore::new(); + let store = ChunkedKeyringStore::new(mock.clone()); + + let value = value_of_utf16_units(MAX_ENTRY_UTF16_UNITS * 2 + 100); + store.set("svc", "acct", &value).unwrap(); + + // Primary entry holds the marker, chunks hold the payload. + let primary = mock.get("svc", "acct").unwrap().unwrap(); + assert_eq!( + primary, + format!("{CHUNK_MARKER_PREFIX}3:{}", value.encode_utf16().count()) + ); + for ((_, account), chunk) in store_snapshot_chunks(&mock) { + assert_ne!(account, "acct"); + assert!( + chunk.encode_utf16().count() <= MAX_ENTRY_UTF16_UNITS, + "chunk {account} exceeds the per-entry UTF-16 budget" + ); + } + + assert_eq!(store.get("svc", "acct").unwrap().as_deref(), Some(value.as_str())); + } + + /// Chunk entries (everything except the primary marker entry). + fn store_snapshot_chunks(mock: &MockKeyringStore) -> Vec<((String, String), String)> { + mock.snapshot() + .into_iter() + .filter(|((_, account), _)| account != "acct") + .collect() + } + + #[test] + fn chunked_store_never_splits_surrogate_pairs() { + let mock = MockKeyringStore::new(); + let store = ChunkedKeyringStore::new(mock.clone()); + + // '𝄞' is 2 UTF-16 code units. The leading 1-unit char makes the + // per-entry budget run out mid-pair, so the splitter must break + // one unit early rather than divide a surrogate pair. + let mut value = String::from("x"); + value.extend(std::iter::repeat_n('𝄞', MAX_ENTRY_UTF16_UNITS)); + store.set("svc", "acct", &value).unwrap(); + assert_eq!(store.get("svc", "acct").unwrap().as_deref(), Some(value.as_str())); + } + + #[test] + fn chunked_store_overwrite_with_smaller_value_removes_stale_chunks() { + let mock = MockKeyringStore::new(); + let store = ChunkedKeyringStore::new(mock.clone()); + + let big = value_of_utf16_units(MAX_ENTRY_UTF16_UNITS * 3); + store.set("svc", "acct", &big).unwrap(); + assert_eq!(mock.snapshot().len(), 4); // marker + 3 chunks + + store.set("svc", "acct", "small").unwrap(); + assert_eq!(store.get("svc", "acct").unwrap().as_deref(), Some("small")); + assert_eq!(mock.snapshot().len(), 1, "stale chunk entries must be removed"); + } + + #[test] + fn chunked_store_delete_removes_all_chunks() { + let mock = MockKeyringStore::new(); + let store = ChunkedKeyringStore::new(mock.clone()); + + let big = value_of_utf16_units(MAX_ENTRY_UTF16_UNITS * 2); + store.set("svc", "acct", &big).unwrap(); + store.delete("svc", "acct").unwrap(); + + assert_eq!(store.get("svc", "acct").unwrap(), None); + assert!(mock.snapshot().is_empty(), "delete must remove marker and chunks"); + } + + #[test] + fn chunked_store_reads_plain_value_from_older_binary() { + let mock = MockKeyringStore::new(); + // An older binary wrote directly, without the chunking wrapper. + mock.set("svc", "acct", "legacy-token").unwrap(); + + let store = ChunkedKeyringStore::new(mock); + assert_eq!(store.get("svc", "acct").unwrap().as_deref(), Some("legacy-token")); + } + + #[test] + fn chunked_store_missing_chunk_treated_as_absent() { + let mock = MockKeyringStore::new(); + let store = ChunkedKeyringStore::new(mock.clone()); + + let big = value_of_utf16_units(MAX_ENTRY_UTF16_UNITS * 2); + store.set("svc", "acct", &big).unwrap(); + // Simulate a user deleting one chunk in a credential-manager UI. + mock.delete("svc", "acct#2").unwrap(); + + assert_eq!(store.get("svc", "acct").unwrap(), None); + } + + #[test] + fn chunked_store_torn_write_detected_by_length() { + let mock = MockKeyringStore::new(); + let store = ChunkedKeyringStore::new(mock.clone()); + + // Three chunks on disk, then a write that dies after replacing only + // the first chunk with a shorter value. The stale marker still says + // three chunks, so `get` would otherwise splice new chunk 1 onto old + // chunks 2-3 and hand back a corrupt token. + let big = value_of_utf16_units(MAX_ENTRY_UTF16_UNITS * 3); + store.set("svc", "acct", &big).unwrap(); + mock.set("svc", "acct#1", "truncated-replacement").unwrap(); + + assert_eq!(store.get("svc", "acct").unwrap(), None); + } + + #[test] + fn chunked_store_marker_without_length_is_not_treated_as_chunked() { + let mock = MockKeyringStore::new(); + // A count-only marker is not a marker this version writes; it must + // not be parsed as one and silently reassemble missing chunks. + mock.set("svc", "acct", &format!("{CHUNK_MARKER_PREFIX}2")) + .unwrap(); + + let store = ChunkedKeyringStore::new(mock); + assert_eq!( + store.get("svc", "acct").unwrap().as_deref(), + Some(format!("{CHUNK_MARKER_PREFIX}2").as_str()) + ); + } + + #[test] + fn chunked_store_write_gating_follows_platform() { + let mock = MockKeyringStore::new(); + let store = ChunkedKeyringStore::for_platform(mock.clone()); + + let big = value_of_utf16_units(MAX_ENTRY_UTF16_UNITS * 2); + store.set("svc", "acct", &big).unwrap(); + + if cfg!(target_os = "windows") { + assert_eq!( + mock.snapshot().len(), + 3, + "Windows must split oversized values: marker + 2 chunks" + ); + } else { + // Byte-identical to a pre-chunking binary, so a downgraded CLI + // still reads a usable credential rather than the marker. + assert_eq!( + mock.snapshot().len(), + 1, + "platforms without a blob limit must store plain" + ); + assert_eq!(mock.get("svc", "acct").unwrap().as_deref(), Some(big.as_str())); + } + + assert_eq!(store.get("svc", "acct").unwrap().as_deref(), Some(big.as_str())); + } + + #[test] + fn chunked_store_reads_chunks_even_when_writes_are_gated() { + let mock = MockKeyringStore::new(); + let big = value_of_utf16_units(MAX_ENTRY_UTF16_UNITS * 2); + // Written by a chunking build. + ChunkedKeyringStore::new(mock.clone()) + .set("svc", "acct", &big) + .unwrap(); + + // A non-chunking wrapper must still reassemble it. + let reader = ChunkedKeyringStore::with_chunk_writes(mock, false); + assert_eq!(reader.get("svc", "acct").unwrap().as_deref(), Some(big.as_str())); + } + + #[test] + fn chunked_store_gated_write_reaps_chunks_from_a_chunking_build() { + let mock = MockKeyringStore::new(); + let big = value_of_utf16_units(MAX_ENTRY_UTF16_UNITS * 3); + ChunkedKeyringStore::new(mock.clone()) + .set("svc", "acct", &big) + .unwrap(); + assert_eq!(mock.snapshot().len(), 4); + + // Re-login on a platform that stores plain must not leave the old + // chunk entries behind. + let plain = ChunkedKeyringStore::with_chunk_writes(mock.clone(), false); + plain.set("svc", "acct", &big).unwrap(); + + assert_eq!(mock.snapshot().len(), 1, "stale chunks must be removed"); + assert_eq!(plain.get("svc", "acct").unwrap().as_deref(), Some(big.as_str())); + } + + #[test] + fn chunked_store_backend_label_delegates() { + let store = ChunkedKeyringStore::new(MockKeyringStore::new()); + assert_eq!(store.backend_label(), "mock (in-memory)"); + } + #[test] fn backend_labels_describe_themselves() { let file = FileKeyringStore::at_root(PathBuf::from("/tmp/xx")); diff --git a/src/auth/login.rs b/src/auth/login.rs index a114d0b..a2c396c 100644 --- a/src/auth/login.rs +++ b/src/auth/login.rs @@ -399,7 +399,16 @@ fn handle_status( login_flows: &[DynLoginFlow], out: &mut W, ) -> Result<(), CliError> { - let as_json = matches.get_flag("json"); + // `--json` is this subcommand's own boolean and predates the global + // `--format`. Honouring only that made `auth status --format json` print + // the human table with no error and no hint — the one command an agent + // needs before anything else, silently ignoring the flag it was taught to + // use everywhere else. `--format` (and the piped default behind it) now + // decides too; `--json` stays as an alias so existing scripts keep working. + let as_json = matches.get_flag("json") + || crate::formatter::OutputPipeline::from_matches(matches, cli_name) + .map(|pipeline| pipeline.format.is_machine_readable()) + .unwrap_or(false); let store = active_store(); let backend = store.backend_label(); diff --git a/src/auth/oauth_common.rs b/src/auth/oauth_common.rs index 080bcbe..5188eaf 100644 --- a/src/auth/oauth_common.rs +++ b/src/auth/oauth_common.rs @@ -224,23 +224,80 @@ pub(crate) fn config_dir() -> Option { } } +/// Unlinks a temp file on drop unless [`TempFileGuard::disarm`]ed. Covers +/// every early return *and* a panic between creating the temp file and +/// renaming it into place. +/// +/// This exists because the temp name is unique per writer. The old shared +/// `auth-keyring.tmp` was self-limiting — a failed write left one stale file +/// that the next write reused — whereas unique names would leak a distinct +/// credential-bearing file on every failure, in a directory nothing prunes. A +/// `SIGKILL` still leaks, since no in-process guard can cover that. +struct TempFileGuard { + path: PathBuf, + armed: bool, +} + +impl TempFileGuard { + fn new(path: PathBuf) -> Self { + Self { path, armed: true } + } + + /// Relinquish the file — call once `rename` has moved it into place. + fn disarm(&mut self) { + self.armed = false; + } +} + +impl Drop for TempFileGuard { + fn drop(&mut self) { + if self.armed { + let _ = std::fs::remove_file(&self.path); + } + } +} + /// Write `data` to `path` atomically: sibling temp file → owner-only /// permissions (0600 on Unix) → rename into place. +/// +/// The temp file name is unique per writer — pid plus a process-local +/// counter. Deriving it from the target alone meant every concurrent writer +/// used the *same* sibling (`auth-keyring.tmp`): whichever one renamed first +/// moved it away, and the rest failed with `ENOENT` from `rename`. That +/// surfaced as intermittent `auth login --with-token` failures across a +/// wire-test suite large enough to run many CLI processes at once, killing a +/// different subset of cases on each run. +/// +/// The pid covers the case that actually bit us (separate CLI processes); the +/// counter covers two writers inside one process, and is what makes the +/// behavior unit-testable without spawning subprocesses. +/// +/// A [`TempFileGuard`] unlinks the temp file if anything between creating it +/// and renaming it fails, so unique names cannot accumulate as orphans. +/// +/// `rename` is still atomic for readers, which is what keeps a partially +/// written credential file unobservable. This only fixes writer-vs-writer +/// collisions on the temp path. `FileKeyringStore::set` remains a +/// read-modify-write of the whole map with no lock, so simultaneous writers +/// can still clobber one another's *entries*; making that safe needs file +/// locking, which is a larger change. pub(crate) fn atomic_write(path: &Path, data: &[u8]) -> Result<(), CliError> { - let tmp = path.with_extension("tmp"); - std::fs::write(&tmp, data).map_err(|e| { - CliError::Auth(format!("Failed to write {}: {e}", tmp.display())) - })?; + static TMP_SEQ: std::sync::atomic::AtomicUsize = std::sync::atomic::AtomicUsize::new(0); + let seq = TMP_SEQ.fetch_add(1, std::sync::atomic::Ordering::Relaxed); + let tmp = path.with_extension(format!("tmp.{}.{}", std::process::id(), seq)); + let mut guard = TempFileGuard::new(tmp.clone()); + std::fs::write(&tmp, data) + .map_err(|e| CliError::Auth(format!("Failed to write {}: {e}", tmp.display())))?; #[cfg(unix)] { use std::os::unix::fs::PermissionsExt; let perms = std::fs::Permissions::from_mode(0o600); let _ = std::fs::set_permissions(&tmp, perms); } - std::fs::rename(&tmp, path).map_err(|e| { - let _ = std::fs::remove_file(&tmp); - CliError::Auth(format!("Failed to rename {}: {e}", tmp.display())) - }) + std::fs::rename(&tmp, path) + .map_err(|e| CliError::Auth(format!("Failed to rename {}: {e}", tmp.display())))?; + guard.disarm(); + Ok(()) } // --------------------------------------------------------------------------- @@ -251,6 +308,101 @@ pub(crate) fn atomic_write(path: &Path, data: &[u8]) -> Result<(), CliError> { mod tests { use super::*; + /// Regression test for the temp-file collision that made + /// `auth login --with-token` fail intermittently: every writer derived the + /// same sibling path from the target, so the first `rename` moved it away + /// and the rest got `ENOENT`. + /// + /// Reverting `atomic_write` to a target-derived temp name fails this with + /// "Failed to rename ...: No such file or directory". + #[test] + fn atomic_write_tolerates_concurrent_writers() { + let dir = tempfile::tempdir().unwrap(); + let target = dir.path().join("auth-keyring.json"); + + let results: Vec> = std::thread::scope(|scope| { + let handles: Vec<_> = (0..16) + .map(|i| { + let target = target.clone(); + scope.spawn(move || atomic_write(&target, format!(r#"{{"writer":{i}}}"#).as_bytes())) + }) + .collect(); + handles.into_iter().map(|h| h.join().unwrap()).collect() + }); + + let failed: Vec = results + .iter() + .filter_map(|r| r.as_ref().err().map(|e| e.to_string())) + .collect(); + assert!(failed.is_empty(), "concurrent writers failed: {failed:?}"); + + // Last writer wins, but the file must always be one writer's complete + // payload — never a mix, and never absent. + let contents = std::fs::read_to_string(&target).unwrap(); + let parsed: serde_json::Value = serde_json::from_str(&contents) + .unwrap_or_else(|e| panic!("target is not valid JSON after concurrent writes: {e} in {contents:?}")); + assert!(parsed.get("writer").is_some(), "unexpected payload: {contents}"); + + // No temp files orphaned in the directory. + let leftovers: Vec = std::fs::read_dir(dir.path()) + .unwrap() + .filter_map(|e| e.ok()) + .map(|e| e.file_name().to_string_lossy().into_owned()) + .filter(|n| n.contains(".tmp")) + .collect(); + assert!(leftovers.is_empty(), "temp files left behind: {leftovers:?}"); + } + + /// A failed write must not leave its temp file behind. Renaming a file onto + /// an existing directory fails on every platform, which drives the error + /// path without mocking the filesystem. + /// + /// The pre-`TempFileGuard` code already cleaned up on a `rename` error, so + /// this pins an invariant rather than catching a regression — see + /// `temp_file_guard_unlinks_unless_disarmed` for the guard's own coverage. + #[test] + fn atomic_write_cleans_up_temp_file_on_failure() { + let dir = tempfile::tempdir().unwrap(); + + // The target is a *directory*, so `rename` cannot replace it. + let target = dir.path().join("auth-keyring.json"); + std::fs::create_dir(&target).unwrap(); + std::fs::write(target.join("occupant"), b"x").unwrap(); + + let result = atomic_write(&target, br#"{"writer":0}"#); + assert!(result.is_err(), "expected rename onto a directory to fail"); + + let leftovers: Vec = std::fs::read_dir(dir.path()) + .unwrap() + .filter_map(|e| e.ok()) + .map(|e| e.file_name().to_string_lossy().into_owned()) + .filter(|n| n.contains(".tmp")) + .collect(); + assert!(leftovers.is_empty(), "temp file leaked after a failed write: {leftovers:?}"); + } + + /// Pins [`TempFileGuard`]: armed drops unlink, disarmed drops don't. Drop + /// running on an armed guard is what covers early returns and unwinding + /// panics between `write` and `rename` — paths the old `rename`-only + /// cleanup missed. Deleting the guard or the `disarm()` call fails this. + #[test] + fn temp_file_guard_unlinks_unless_disarmed() { + let dir = tempfile::tempdir().unwrap(); + let path = dir.path().join("auth-keyring.tmp.0.0"); + + std::fs::write(&path, b"x").unwrap(); + drop(TempFileGuard::new(path.clone())); + assert!(!path.exists(), "armed guard must unlink on drop"); + + // The post-`rename` case: the file has been moved away, so the guard + // must not touch whatever now sits at that path. + std::fs::write(&path, b"x").unwrap(); + let mut guard = TempFileGuard::new(path.clone()); + guard.disarm(); + drop(guard); + assert!(path.exists(), "disarmed guard must not unlink"); + } + #[test] fn token_bundle_roundtrip() { let b = TokenBundle::from_token_response("a", Some("r"), Some(3600)); diff --git a/src/auth/oauth_login.rs b/src/auth/oauth_login.rs index 4664748..81fbbd0 100644 --- a/src/auth/oauth_login.rs +++ b/src/auth/oauth_login.rs @@ -445,6 +445,14 @@ pub struct PkceLoginFlow { /// Callback path served by the listener and used in the redirect URI. `None` defaults to /// `/callback`. Set to match a non-`/callback` registered redirect path. redirect_path: Option, + /// Page the loopback listener redirects the browser to once the code is captured. `None` + /// serves the built-in "you can close this tab" HTML inline; `Some(url)` answers `302` with + /// `Location: `, so the customer's own hosted page owns the branding. + success_redirect_url: Option, + /// Page the loopback listener redirects the browser to when authorization fails, with `error` + /// (and `error_description`, when the authorization server sent one) appended as query + /// parameters. `None` serves the built-in failure page inline. + error_redirect_url: Option, token_paste_url: Option, authorization_params: ExtraParams, token_params: ExtraParams, @@ -463,6 +471,8 @@ impl PkceLoginFlow { redirect_backup_ports: Vec::new(), redirect_host: None, redirect_path: None, + success_redirect_url: None, + error_redirect_url: None, token_paste_url: None, authorization_params: Vec::new(), token_params: Vec::new(), @@ -527,6 +537,25 @@ impl PkceLoginFlow { fn redirect_path_str(&self) -> &str { self.redirect_path.as_deref().unwrap_or("/callback") } + /// Redirect the browser to `url` after a successful callback instead of rendering the built-in + /// success page, letting the page live on the customer's own site. + pub fn success_redirect_url(mut self, url: impl Into) -> Self { + self.success_redirect_url = Some(url.into()); + self + } + /// Redirect the browser to `url` when authorization fails instead of rendering the built-in + /// failure page. `error` and `error_description` are appended so the page can explain what + /// happened. + pub fn error_redirect_url(mut self, url: impl Into) -> Self { + self.error_redirect_url = Some(url.into()); + self + } + fn callback_pages(&self) -> CallbackPages<'_> { + CallbackPages { + success_redirect_url: self.success_redirect_url.as_deref(), + error_redirect_url: self.error_redirect_url.as_deref(), + } + } pub fn token_paste_url(mut self, v: impl Into) -> Self { self.token_paste_url = Some(v.into()); self @@ -731,25 +760,57 @@ async fn run_pkce(mut flow: PkceLoginFlow, ctx: LoginContext) -> Result<(), CliE let _ = webbrowser::open(&url); } - // Wait for the browser to hit /callback with code+state. - let (code, received_state) = match accept_callback(&listener, &redirect_path).await { + // Wait for the browser to hit /callback with code+state. The listener checks the `state` it + // received against the one we sent, but answers the browser only for a callback it rejects — + // a usable one comes back still connected, via the responder. + let (code, responder) = match accept_callback(&listener, &redirect_path, &state, flow.callback_pages()).await { Ok(v) => v, Err(e) => return Err(e), }; - if received_state != state { - return Err(CliError::Auth(format!( - "OAuth state mismatch (expected `{state}`, got `{received_state}`) — possible CSRF; aborting" - ))); + // Everything the browser is still waiting on. Funneled through one result so the page it lands + // on is chosen from what actually happened, rather than from having received a code. + let outcome = exchange_code_and_store(&flow, &ctx, &code, &verifier).await; + match outcome { + Ok(()) => responder.success().await, + Err(e) => { + responder + .failure("server_error", Some(POST_CALLBACK_FAILURE_DESCRIPTION)) + .await; + return Err(e); + } + } + + { + let mut err = std::io::stderr().lock(); + let _ = writeln!( + err, + "{}", + crate::auth::login::green(&format!( + "✓ Authenticated. Stored credential in {}.", + active_store().backend_label() + )) + ); } + Ok(()) +} - // Exchange the code. +/// Redeem the authorization code and persist the credential — the part of the login that happens +/// after the browser callback and decides whether it actually succeeded. Split out of `run_pkce` +/// so its failures are a single `Result` the caller can answer the waiting browser from, instead +/// of a handful of `?`s that would return past it. +async fn exchange_code_and_store( + flow: &PkceLoginFlow, + ctx: &LoginContext, + code: &str, + verifier: &str, +) -> Result<(), CliError> { let http = token_http_client()?; let redirect_uri = flow.redirect_uri(); let mut form: Vec<(String, String)> = vec![ ("grant_type".to_string(), "authorization_code".to_string()), - ("code".to_string(), code.clone()), - ("code_verifier".to_string(), verifier.clone()), + ("code".to_string(), code.to_string()), + ("code_verifier".to_string(), verifier.to_string()), ("client_id".to_string(), flow.client_id.clone()), ("redirect_uri".to_string(), redirect_uri), ]; @@ -785,46 +846,101 @@ async fn run_pkce(mut flow: PkceLoginFlow, ctx: LoginContext) -> Result<(), CliE ok.expires_in, ); active_store().set(&ctx.cli_name, &flow.scheme, &bundle.to_keyring_value()?)?; + Ok(()) +} - { - let mut err = std::io::stderr().lock(); - let _ = writeln!( - err, - "{}", - crate::auth::login::green(&format!( - "✓ Authenticated. Stored credential in {}.", - active_store().backend_label() - )) - ); +/// Where the loopback listener sends the browser once the callback has been handled. Empty by +/// default, in which case the listener renders its own page inline. +#[derive(Clone, Copy, Default)] +struct CallbackPages<'a> { + success_redirect_url: Option<&'a str>, + error_redirect_url: Option<&'a str>, +} + +impl<'a> CallbackPages<'a> { + /// A configured URL is interpolated straight into a `Location` header, so a value carrying + /// CR/LF (or any other control character) could smuggle in extra headers. `fern check` rejects + /// such values, so this only fires if one reaches a built binary anyway — in which case we + /// serve the built-in page instead of emitting a malformed response. + fn success(&self) -> Option<&'a str> { + self.success_redirect_url.filter(|url| is_header_safe_url(url)) + } + fn error(&self) -> Option<&'a str> { + self.error_redirect_url.filter(|url| is_header_safe_url(url)) + } +} + +/// The still-open loopback connection, handed back to the caller so the browser is answered only +/// once the login has actually finished. Responding from inside the listener would mean deciding +/// the outcome before the token exchange and the keyring write have happened — every page that +/// says "you're logged in", ours or a customer's, would be a guess. +/// +/// The browser waits in the meantime. That is bounded: the token exchange carries a 10s connect / +/// 30s total timeout, and the keyring write is local. +struct CallbackResponder<'a> { + socket: tokio::net::TcpStream, + pages: CallbackPages<'a>, +} + +impl<'a> CallbackResponder<'a> { + /// The login completed and the credential is stored. Consumes `self`: one response per + /// connection, and it closes. + async fn success(mut self) { + match self.pages.success() { + Some(url) => { + let _ = write_redirect(&mut self.socket, url).await; + } + None => { + let _ = + write_response_html(&mut self.socket, 200, &render_callback_page(CallbackOutcome::Success)).await; + } + } + } + + /// The callback was fine but the login failed after it — a rejected token exchange, or a + /// credential we couldn't persist. + async fn failure(mut self, error: &str, description: Option<&str>) { + write_failure(&mut self.socket, &self.pages, error, description).await; } - Ok(()) } -const CALLBACK_RESPONSE_BODY: &str = "\ -Authenticated\ -\ -

You can close this tab.

\ -

The CLI received your authorization code.

\ -"; +/// What a hosted error page is told when the callback was accepted but the login failed afterwards. +/// The reason itself stays in the terminal: a token-endpoint body is not something to forward into +/// a URL, and the user is being sent back to the terminal to read it anyway. +const POST_CALLBACK_FAILURE_DESCRIPTION: &str = "The CLI could not complete the login. Check your terminal for details."; -/// Accept one HTTP request on the loopback listener, parse `?code=…&state=…` -/// from the request line, send a small HTML response, return `(code, state)`. /// Cap on how long the PKCE listener waits for the browser callback /// before bailing. Five minutes matches typical OAuth authorization-code /// lifetimes — if the user abandoned the browser tab or got distracted, /// surfacing a clear timeout beats hanging silently. const PKCE_CALLBACK_TIMEOUT: Duration = Duration::from_secs(300); -async fn accept_callback(listener: &TcpListener, expected_path: &str) -> Result<(String, String), CliError> { - accept_callback_with_timeout(listener, expected_path, PKCE_CALLBACK_TIMEOUT).await +/// Accept one HTTP request on the loopback listener, parse `?code=…&state=…` from the request +/// line, and check `state` against `expected_state`. A callback we can't use is answered here and +/// returns an error; a usable one returns the code plus the still-open connection, so the caller +/// answers the browser once it knows whether the login actually worked. +async fn accept_callback<'a>( + listener: &TcpListener, + expected_path: &str, + expected_state: &str, + pages: CallbackPages<'a>, +) -> Result<(String, CallbackResponder<'a>), CliError> { + accept_callback_with_timeout(listener, expected_path, expected_state, pages, PKCE_CALLBACK_TIMEOUT).await } -async fn accept_callback_with_timeout( +async fn accept_callback_with_timeout<'a>( listener: &TcpListener, expected_path: &str, + expected_state: &str, + pages: CallbackPages<'a>, timeout: Duration, -) -> Result<(String, String), CliError> { - match tokio::time::timeout(timeout, accept_callback_inner(listener, expected_path)).await { +) -> Result<(String, CallbackResponder<'a>), CliError> { + match tokio::time::timeout( + timeout, + accept_callback_inner(listener, expected_path, expected_state, pages), + ) + .await + { Ok(r) => r, Err(_) => Err(CliError::Auth(format!( "Timed out waiting for the OAuth callback after {}s. \ @@ -835,7 +951,12 @@ async fn accept_callback_with_timeout( } } -async fn accept_callback_inner(listener: &TcpListener, expected_path: &str) -> Result<(String, String), CliError> { +async fn accept_callback_inner<'a>( + listener: &TcpListener, + expected_path: &str, + expected_state: &str, + pages: CallbackPages<'a>, +) -> Result<(String, CallbackResponder<'a>), CliError> { // Single-shot accept. If the browser hits us with a noisy preflight // (favicon, etc.) we skip and accept the next; cap at 8 attempts. for _ in 0..8 { @@ -872,31 +993,64 @@ async fn accept_callback_inner(listener: &TcpListener, expected_path: &str) -> R let mut code = None; let mut state = None; let mut error_param = None; + let mut error_description = None; for (k, v) in form_urlencoded::parse(qs.as_bytes()) { match k.as_ref() { "code" => code = Some(v.into_owned()), "state" => state = Some(v.into_owned()), "error" => error_param = Some(v.into_owned()), + "error_description" => error_description = Some(v.into_owned()), _ => {} } } if let Some(e) = error_param { - let _ = write_response(&mut socket, 400, "authorization failed").await; - return Err(CliError::Auth(format!( - "Authorization server returned error: {e}" - ))); + write_failure(&mut socket, &pages, &e, error_description.as_deref()).await; + return Err(CliError::Auth(match error_description { + Some(description) => format!("Authorization server returned error: {e} ({description})"), + None => format!("Authorization server returned error: {e}"), + })); } - let (Some(code), Some(state)) = (code, state) else { - let _ = write_response(&mut socket, 400, "missing code or state").await; - return Err(CliError::Auth( - "callback missing `code` or `state` query parameter".to_string(), - )); + let (code, state) = match (code, state) { + (Some(code), Some(state)) => (code, state), + (code, _) => { + // No `error` from the authorization server, but the callback is unusable. Synthesize + // the OAuth error code for it so a hosted error page always has something to render. + let missing = if code.is_none() { "code" } else { "state" }; + write_failure( + &mut socket, + &pages, + "invalid_request", + Some(&format!("The callback was missing its `{missing}` parameter.")), + ) + .await; + return Err(CliError::Auth(format!( + "callback missing `{missing}` query parameter" + ))); + } }; - let _ = write_response_html(&mut socket, 200, CALLBACK_RESPONSE_BODY).await; - return Ok((code, state)); + if state != expected_state { + // Possible CSRF. The browser gets the failure page like any other unusable callback — + // telling it "you're all set" for a callback we're about to reject would be a lie, and + // on a configured page a branded one. Neither state value is forwarded: the one we sent + // is a per-login nonce and has no business leaving the loopback. + write_failure( + &mut socket, + &pages, + "invalid_request", + Some("The callback `state` parameter did not match the one the CLI sent."), + ) + .await; + return Err(CliError::Auth(format!( + "OAuth state mismatch (expected `{expected_state}`, got `{state}`) — possible CSRF; aborting" + ))); + } + + // A usable callback. The browser stays connected and unanswered until the caller has + // exchanged the code and stored the credential. + return Ok((code, CallbackResponder { socket, pages })); } Err(CliError::Auth( "Too many invalid requests on the loopback listener; giving up".to_string(), @@ -922,6 +1076,72 @@ async fn write_response(socket: &mut tokio::net::TcpStream, status: u16, msg: &s socket.flush().await } +/// A configured redirect target is interpolated straight into a `Location` header, so anything +/// carrying CR/LF (or other control characters) could smuggle in extra headers. The generator +/// rejects such values at codegen time; this is the runtime backstop — an unsafe URL falls back to +/// the built-in success page rather than emitting a malformed response. +fn is_header_safe_url(url: &str) -> bool { + !url.is_empty() && !url.chars().any(char::is_control) +} + +/// Answer a failed callback: hand the browser off to the configured error page (carrying `error` / +/// `error_description` so it can explain what happened), or render the built-in failure page. +async fn write_failure( + socket: &mut tokio::net::TcpStream, + pages: &CallbackPages<'_>, + error: &str, + description: Option<&str>, +) { + match pages.error() { + Some(url) => { + let _ = write_redirect(socket, &build_error_redirect(url, error, description)).await; + } + None => { + let page = render_callback_page(CallbackOutcome::Failure { error, description }); + let _ = write_response_html(socket, 400, &page).await; + } + } +} + +/// Append `error` (and `error_description`, when the authorization server sent one) to the +/// configured error page URL. Parameters are serialized as `application/x-www-form-urlencoded` +/// rather than concatenated, merged into any query string the URL already carries, and inserted +/// before a fragment so `…/error#recover` stays a fragment. +fn build_error_redirect(url: &str, error: &str, description: Option<&str>) -> String { + let mut serializer = form_urlencoded::Serializer::new(String::new()); + serializer.append_pair("error", error); + if let Some(description) = description { + serializer.append_pair("error_description", description); + } + let params = serializer.finish(); + + let (base, fragment) = match url.split_once('#') { + Some((base, fragment)) => (base, Some(fragment)), + None => (url, None), + }; + let separator = if base.contains('?') { '&' } else { '?' }; + match fragment { + Some(fragment) => format!("{base}{separator}{params}#{fragment}"), + None => format!("{base}{separator}{params}"), + } +} + +/// Hand the browser off to an externally hosted page. The body is a courtesy for clients that +/// don't follow the redirect; browsers never render it. +async fn write_redirect(socket: &mut tokio::net::TcpStream, location: &str) -> std::io::Result<()> { + let body = format!( + "Continue", + html_escape(location) + ); + let resp = format!( + "HTTP/1.1 302 {}\r\nLocation: {location}\r\nContent-Type: text/html; charset=utf-8\r\nContent-Length: {}\r\nConnection: close\r\n\r\n{body}", + status_phrase(302), + body.len() + ); + socket.write_all(resp.as_bytes()).await?; + socket.flush().await +} + async fn write_response_html(socket: &mut tokio::net::TcpStream, status: u16, body: &str) -> std::io::Result<()> { let resp = format!( "HTTP/1.1 {status} {}\r\nContent-Type: text/html; charset=utf-8\r\nContent-Length: {}\r\nConnection: close\r\n\r\n{body}", @@ -932,9 +1152,109 @@ async fn write_response_html(socket: &mut tokio::net::TcpStream, status: u16, bo socket.flush().await } +/// What the loopback listener is rendering a page for. Carried instead of a pre-rendered string so +/// the two states share one template. +#[derive(Clone, Copy)] +enum CallbackOutcome<'a> { + Success, + Failure { + error: &'a str, + description: Option<&'a str>, + }, +} + +/// The Fern leaf, as inline SVG. Everything in these pages is inlined on purpose: the loopback +/// listener has no CDN behind it, and a hosted asset is one corp proxy away from a broken image on +/// the last screen of `auth login`. +const FERN_LEAF_SVG: &str = ""; + +/// The pages the loopback listener serves when no hosted page is configured. Self-contained: no +/// external stylesheet, font, or image request, so it renders identically offline and behind a +/// proxy. +fn render_callback_page(outcome: CallbackOutcome<'_>) -> String { + let (title, glyph, heading, detail) = match outcome { + CallbackOutcome::Success => ( + "Authentication complete", + "✓", + "You're all set", + "The CLI received your authorization. You can close this tab and return to your terminal." + .to_string(), + ), + CallbackOutcome::Failure { error, description } => ( + "Authentication failed", + "!", + "Authorization didn't complete", + match description { + Some(description) => format!( + "{} ({}) Close this tab and run auth login again.", + html_escape(description), + html_escape(error) + ), + None => format!( + "The authorization server returned {}. Close this tab and run auth login again.", + html_escape(error) + ), + }, + ), + }; + let status_class = match outcome { + CallbackOutcome::Success => "ok", + CallbackOutcome::Failure { .. } => "bad", + }; + format!( + "\n\ +\ +{title}\ +
{glyph}
\ +

{heading}

{detail}

\ +\ +
" + ) +} + +/// Escape text interpolated into the built-in pages. `error` / `error_description` come off the +/// query string, so they are attacker-influenced in the same way any callback parameter is. +fn html_escape(value: &str) -> String { + let mut escaped = String::with_capacity(value.len()); + for ch in value.chars() { + match ch { + '&' => escaped.push_str("&"), + '<' => escaped.push_str("<"), + '>' => escaped.push_str(">"), + '"' => escaped.push_str("""), + '\'' => escaped.push_str("'"), + _ => escaped.push(ch), + } + } + escaped +} + fn status_phrase(s: u16) -> &'static str { match s { 200 => "OK", + 302 => "Found", 400 => "Bad Request", 404 => "Not Found", _ => "Status", @@ -1716,6 +2036,34 @@ mod tests { p } + /// Mirror `run_pkce`'s happy path: accept the callback, then — standing in for a token exchange + /// and keyring write that succeeded — answer the browser. Tests that assert on the success + /// response have to go through this, because nothing is written until the caller says so. + async fn accept_and_confirm( + listener: &TcpListener, + expected_path: &str, + expected_state: &str, + pages: CallbackPages<'_>, + ) -> Result { + let (code, responder) = accept_callback(listener, expected_path, expected_state, pages).await?; + responder.success().await; + Ok(code) + } + + /// Mirror `run_pkce`'s post-callback failure path: the callback was fine, the login wasn't. + async fn accept_and_fail( + listener: &TcpListener, + expected_path: &str, + expected_state: &str, + pages: CallbackPages<'_>, + ) -> Result { + let (code, responder) = accept_callback(listener, expected_path, expected_state, pages).await?; + responder + .failure("server_error", Some(POST_CALLBACK_FAILURE_DESCRIPTION)) + .await; + Ok(code) + } + #[tokio::test(flavor = "multi_thread")] async fn pkce_loopback_times_out_when_no_callback_arrives() { // When the browser never hits /callback (user closed tab, etc.), @@ -1725,9 +2073,16 @@ mod tests { // of waiting the production 5-minute deadline. let port = pick_free_port(); let listener = TcpListener::bind(("127.0.0.1", port)).await.unwrap(); - let err = accept_callback_with_timeout(&listener, "/callback", Duration::from_millis(100)) - .await - .expect_err("expected timeout when no browser callback arrives"); + let err = accept_callback_with_timeout( + &listener, + "/callback", + "state-xyz", + CallbackPages::default(), + Duration::from_millis(100), + ) + .await + .map(|(code, _responder)| code) + .expect_err("expected timeout when no browser callback arrives"); let msg = format!("{err}"); assert!( msg.contains("Timed out") && msg.contains("auth login"), @@ -1736,12 +2091,14 @@ mod tests { } #[tokio::test(flavor = "multi_thread")] - async fn pkce_loopback_handshake_returns_code_and_state() { + async fn pkce_loopback_handshake_returns_code() { let port = pick_free_port(); let listener = TcpListener::bind(("127.0.0.1", port)).await.unwrap(); // Spawn the accept task. - let acceptor = tokio::spawn(async move { accept_callback(&listener, "/callback").await }); + let acceptor = tokio::spawn(async move { + accept_and_confirm(&listener, "/callback", "state-xyz", CallbackPages::default()).await + }); // Act as the browser. tokio::time::sleep(Duration::from_millis(50)).await; @@ -1753,16 +2110,295 @@ mod tests { .await .unwrap(); - let (code, state) = acceptor.await.unwrap().unwrap(); - assert_eq!(code, "auth-code-abc"); - assert_eq!(state, "state-xyz"); + assert_eq!(acceptor.await.unwrap().unwrap(), "auth-code-abc"); + } + + #[tokio::test(flavor = "multi_thread")] + async fn pkce_loopback_redirects_to_configured_success_page() { + let port = pick_free_port(); + let listener = TcpListener::bind(("127.0.0.1", port)).await.unwrap(); + let acceptor = tokio::spawn(async move { + accept_and_confirm( + &listener, + "/callback", + "state-xyz", + CallbackPages { + success_redirect_url: Some("https://acme.com/cli/welcome"), + error_redirect_url: None, + }, + ) + .await + }); + + tokio::time::sleep(Duration::from_millis(50)).await; + // No redirect following: the 302 itself is what we assert on. + let resp = reqwest::Client::builder() + .redirect(reqwest::redirect::Policy::none()) + .build() + .unwrap() + .get(format!( + "http://127.0.0.1:{port}/callback?code=auth-code-abc&state=state-xyz" + )) + .send() + .await + .unwrap(); + + assert_eq!(resp.status().as_u16(), 302); + assert_eq!( + resp.headers().get("location").unwrap().to_str().unwrap(), + "https://acme.com/cli/welcome" + ); + assert_eq!(acceptor.await.unwrap().unwrap(), "auth-code-abc"); + } + + #[tokio::test(flavor = "multi_thread")] + async fn pkce_loopback_writes_nothing_until_the_caller_answers() { + // The invariant the responder buys: a valid callback gets *no* response while the caller is + // still exchanging the code. Drive the browser from a task so we can observe it waiting. + let port = pick_free_port(); + let listener = TcpListener::bind(("127.0.0.1", port)).await.unwrap(); + let mut browser = tokio::spawn(async move { + reqwest::Client::new() + .get(format!("http://127.0.0.1:{port}/callback?code=c&state=s")) + .send() + .await + }); + + let (code, responder) = accept_callback(&listener, "/callback", "s", CallbackPages::default()) + .await + .expect("valid callback"); + assert_eq!(code, "c"); + + // Still connected, still unanswered — this is where the token exchange would be running. + assert!( + tokio::time::timeout(Duration::from_millis(200), &mut browser) + .await + .is_err(), + "browser was answered before the caller confirmed the login" + ); + + responder.success().await; + let resp = browser.await.unwrap().unwrap(); + assert_eq!(resp.status().as_u16(), 200); + assert!(resp.text().await.unwrap().contains("You're all set")); + } + + #[tokio::test(flavor = "multi_thread")] + async fn pkce_loopback_withholds_the_success_page_until_the_caller_confirms() { + // The reason the responder exists. `code` and `state` are both valid, so the old listener + // would have answered `200` here and then let the terminal report a failed token exchange. + // Now nothing is written until the caller decides, and a caller that failed gets the + // failure page — on a configured error URL, not the configured success one. + let port = pick_free_port(); + let listener = TcpListener::bind(("127.0.0.1", port)).await.unwrap(); + let pages = CallbackPages { + success_redirect_url: Some("https://acme.com/cli/success"), + error_redirect_url: Some("https://acme.com/cli/error"), + }; + let acceptor = tokio::spawn(async move { accept_and_fail(&listener, "/callback", "state-xyz", pages).await }); + + tokio::time::sleep(Duration::from_millis(50)).await; + let resp = reqwest::Client::builder() + .redirect(reqwest::redirect::Policy::none()) + .build() + .unwrap() + .get(format!( + "http://127.0.0.1:{port}/callback?code=auth-code-abc&state=state-xyz" + )) + .send() + .await + .unwrap(); + + assert_eq!(resp.status().as_u16(), 302); + assert_eq!( + resp.headers().get("location").unwrap().to_str().unwrap(), + "https://acme.com/cli/error?error=server_error\ + &error_description=The+CLI+could+not+complete+the+login.+Check+your+terminal+for+details." + ); + // The callback itself was fine — the code came back for the caller to try to redeem. + assert_eq!(acceptor.await.unwrap().unwrap(), "auth-code-abc"); + } + + #[tokio::test(flavor = "multi_thread")] + async fn pkce_loopback_builtin_failure_page_when_login_fails_after_a_valid_callback() { + // Same, with no hosted pages configured: the built-in failure page, not the success one. + let port = pick_free_port(); + let listener = TcpListener::bind(("127.0.0.1", port)).await.unwrap(); + let acceptor = tokio::spawn(async move { + accept_and_fail(&listener, "/callback", "state-xyz", CallbackPages::default()).await + }); + + tokio::time::sleep(Duration::from_millis(50)).await; + let resp = reqwest::Client::new() + .get(format!( + "http://127.0.0.1:{port}/callback?code=auth-code-abc&state=state-xyz" + )) + .send() + .await + .unwrap(); + + assert_eq!(resp.status().as_u16(), 400); + let body = resp.text().await.unwrap(); + assert!(body.contains("Authorization didn't complete"), "{body}"); + assert!(!body.contains("You're all set"), "{body}"); + assert_eq!(acceptor.await.unwrap().unwrap(), "auth-code-abc"); + } + + #[tokio::test(flavor = "multi_thread")] + async fn pkce_loopback_falls_back_to_builtin_page_for_unsafe_redirect() { + let port = pick_free_port(); + let listener = TcpListener::bind(("127.0.0.1", port)).await.unwrap(); + let acceptor = tokio::spawn(async move { + accept_and_confirm( + &listener, + "/callback", + "state-xyz", + CallbackPages { + success_redirect_url: Some("https://acme.com/\r\nX-Injected: 1"), + error_redirect_url: None, + }, + ) + .await + }); + + tokio::time::sleep(Duration::from_millis(50)).await; + let resp = reqwest::Client::builder() + .redirect(reqwest::redirect::Policy::none()) + .build() + .unwrap() + .get(format!( + "http://127.0.0.1:{port}/callback?code=auth-code-abc&state=state-xyz" + )) + .send() + .await + .unwrap(); + + assert_eq!(resp.status().as_u16(), 200); + assert!(resp.headers().get("x-injected").is_none()); + assert!(resp.text().await.unwrap().contains("You're all set")); + assert!(acceptor.await.unwrap().is_ok()); + } + + #[tokio::test(flavor = "multi_thread")] + async fn pkce_loopback_redirects_to_configured_error_page_with_params() { + let port = pick_free_port(); + let listener = TcpListener::bind(("127.0.0.1", port)).await.unwrap(); + let acceptor = tokio::spawn(async move { + accept_and_confirm( + &listener, + "/callback", + "state-xyz", + CallbackPages { + success_redirect_url: None, + error_redirect_url: Some("https://acme.com/cli/error"), + }, + ) + .await + }); + + tokio::time::sleep(Duration::from_millis(50)).await; + let resp = reqwest::Client::builder() + .redirect(reqwest::redirect::Policy::none()) + .build() + .unwrap() + .get(format!( + "http://127.0.0.1:{port}/callback?error=access_denied&error_description=User%20denied%20the%20request" + )) + .send() + .await + .unwrap(); + + assert_eq!(resp.status().as_u16(), 302); + assert_eq!( + resp.headers().get("location").unwrap().to_str().unwrap(), + "https://acme.com/cli/error?error=access_denied&error_description=User+denied+the+request" + ); + // The redirect is cosmetic: login still fails, and the terminal still explains why. + let err = format!("{}", acceptor.await.unwrap().expect_err("denied authorization must fail")); + assert!(err.contains("access_denied"), "{err}"); + } + + #[tokio::test(flavor = "multi_thread")] + async fn pkce_loopback_error_page_renders_server_error_escaped() { + let port = pick_free_port(); + let listener = TcpListener::bind(("127.0.0.1", port)).await.unwrap(); + let acceptor = tokio::spawn(async move { + accept_and_confirm(&listener, "/callback", "state-xyz", CallbackPages::default()).await + }); + + tokio::time::sleep(Duration::from_millis(50)).await; + let resp = reqwest::Client::new() + .get(format!( + "http://127.0.0.1:{port}/callback?error=access_denied&error_description=%3Cscript%3Ealert(1)%3C/script%3E" + )) + .send() + .await + .unwrap(); + + assert_eq!(resp.status().as_u16(), 400); + let body = resp.text().await.unwrap(); + assert!(body.contains("Authorization didn't complete"), "{body}"); + assert!(body.contains("Built with"), "{body}"); + assert!(!body.contains("