diff --git a/.python-version b/.python-version
deleted file mode 100644
index 43077b2..0000000
--- a/.python-version
+++ /dev/null
@@ -1 +0,0 @@
-3.9.18
diff --git a/.stats.yml b/.stats.yml
index 2dd3fee..2f1759e 100644
--- a/.stats.yml
+++ b/.stats.yml
@@ -1,4 +1 @@
-configured_endpoints: 30
-openapi_spec_url: https://storage.googleapis.com/stainless-sdk-openapi-specs/beeper/beeper-desktop-api-c08c14bb754b4cb0e02b21fabb680469368286be339dec0aaa8c69d04a1f021a.yml
-openapi_spec_hash: a10246aaf7cdc33b682fc245bd5f893b
-config_hash: 72f9d43b9b51a5da912e9f3730e53ae2
+configured_endpoints: 56
diff --git a/api.md b/api.md
index 4818056..561079d 100644
--- a/api.md
+++ b/api.md
@@ -1,7 +1,15 @@
# Shared Types
```python
-from beeper_desktop_api.types import Attachment, Error, Message, Reaction, User
+from beeper_desktop_api.types import (
+ APIError,
+ AppStateSnapshot,
+ Attachment,
+ Error,
+ Message,
+ Reaction,
+ User,
+)
```
# BeeperDesktop
@@ -22,11 +30,17 @@ Methods:
Types:
```python
-from beeper_desktop_api.types import Account, AccountListResponse
+from beeper_desktop_api.types import (
+ Account,
+ AccountBridge,
+ AccountRetrieveResponse,
+ AccountListResponse,
+)
```
Methods:
+- client.accounts.retrieve(account_id) -> AccountRetrieveResponse
- client.accounts.list() -> AccountListResponse
## Contacts
@@ -42,6 +56,65 @@ Methods:
- client.accounts.contacts.list(account_id, \*\*params) -> SyncCursorSearch[User]
- client.accounts.contacts.search(account_id, \*\*params) -> ContactSearchResponse
+# Bridges
+
+Types:
+
+```python
+from beeper_desktop_api.types import (
+ Bridge,
+ CookieField,
+ DisappearingTimerCapability,
+ GroupFieldCapability,
+ GroupTypeCapabilities,
+ LoginFlow,
+ LoginInputField,
+ LoginSession,
+ ProvisioningCapabilities,
+ ResolveIdentifierCapabilities,
+ BridgeRetrieveResponse,
+ BridgeListResponse,
+)
+```
+
+Methods:
+
+- client.bridges.retrieve(bridge_id) -> BridgeRetrieveResponse
+- client.bridges.list() -> BridgeListResponse
+- client.bridges.retrieve_capabilities(bridge_id) -> ProvisioningCapabilities
+
+## LoginFlows
+
+Types:
+
+```python
+from beeper_desktop_api.types.bridges import LoginFlowListResponse
+```
+
+Methods:
+
+- client.bridges.login_flows.list(bridge_id) -> LoginFlowListResponse
+
+## LoginSessions
+
+Types:
+
+```python
+from beeper_desktop_api.types.bridges import LoginSessionCancelResponse
+```
+
+Methods:
+
+- client.bridges.login_sessions.create(bridge_id, \*\*params) -> LoginSession
+- client.bridges.login_sessions.retrieve(login_session_id, \*, bridge_id) -> LoginSession
+- client.bridges.login_sessions.cancel(login_session_id, \*, bridge_id) -> LoginSessionCancelResponse
+
+### Steps
+
+Methods:
+
+- client.bridges.login_sessions.steps.submit(step_id, \*, bridge_id, login_session_id, \*\*params) -> LoginSession
+
# Chats
Types:
@@ -132,3 +205,112 @@ from beeper_desktop_api.types import InfoRetrieveResponse
Methods:
- client.info.retrieve() -> InfoRetrieveResponse
+
+# App
+
+Types:
+
+```python
+from beeper_desktop_api.types import Verification, AppSessionResponse
+```
+
+Methods:
+
+- client.app.session() -> AppSessionResponse
+
+## Login
+
+Types:
+
+```python
+from beeper_desktop_api.types.app import (
+ LoginRegisterResponse,
+ LoginResponseResponse,
+ LoginStartResponse,
+)
+```
+
+Methods:
+
+- client.app.login.email(\*\*params) -> None
+- client.app.login.register(\*\*params) -> LoginRegisterResponse
+- client.app.login.response(\*\*params) -> LoginResponseResponse
+- client.app.login.start() -> LoginStartResponse
+
+### Verification
+
+#### RecoveryKey
+
+Types:
+
+```python
+from beeper_desktop_api.types.app.login.verification import RecoveryKeyVerifyResponse
+```
+
+Methods:
+
+- client.app.login.verification.recovery_key.verify(\*\*params) -> RecoveryKeyVerifyResponse
+
+##### Reset
+
+Types:
+
+```python
+from beeper_desktop_api.types.app.login.verification.recovery_key import (
+ ResetCreateResponse,
+ ResetConfirmResponse,
+)
+```
+
+Methods:
+
+- client.app.login.verification.recovery_key.reset.create(\*\*params) -> ResetCreateResponse
+- client.app.login.verification.recovery_key.reset.confirm(\*\*params) -> ResetConfirmResponse
+
+## Verifications
+
+Types:
+
+```python
+from beeper_desktop_api.types.app import (
+ VerificationCreateResponse,
+ VerificationRetrieveResponse,
+ VerificationListResponse,
+ VerificationAcceptResponse,
+ VerificationCancelResponse,
+)
+```
+
+Methods:
+
+- client.app.verifications.create(\*\*params) -> VerificationCreateResponse
+- client.app.verifications.retrieve(verification_id) -> VerificationRetrieveResponse
+- client.app.verifications.list() -> VerificationListResponse
+- client.app.verifications.accept(verification_id) -> VerificationAcceptResponse
+- client.app.verifications.cancel(verification_id, \*\*params) -> VerificationCancelResponse
+
+### Qr
+
+Types:
+
+```python
+from beeper_desktop_api.types.app.verifications import QrConfirmScannedResponse, QrScanResponse
+```
+
+Methods:
+
+- client.app.verifications.qr.confirm_scanned(verification_id) -> QrConfirmScannedResponse
+- client.app.verifications.qr.scan(\*\*params) -> QrScanResponse
+
+### SAS
+
+Types:
+
+```python
+from beeper_desktop_api.types.app.verifications import SASConfirmResponse, SASStartResponse
+```
+
+Methods:
+
+- client.app.verifications.sas.confirm(verification_id) -> SASConfirmResponse
+- client.app.verifications.sas.start(verification_id) -> SASStartResponse
diff --git a/release-please-config.json b/release-please-config.json
index fd672c1..7e37250 100644
--- a/release-please-config.json
+++ b/release-please-config.json
@@ -2,7 +2,7 @@
"packages": {
".": {}
},
- "$schema": "https://raw.githubusercontent.com/stainless-api/release-please/main/schemas/config.json",
+ "$schema": "https://raw.githubusercontent.com/googleapis/release-please/main/schemas/config.json",
"include-v-in-tag": true,
"include-component-in-tag": false,
"versioning": "prerelease",
diff --git a/scripts/mock b/scripts/mock
index 9c7c439..bf29172 100755
--- a/scripts/mock
+++ b/scripts/mock
@@ -7,14 +7,15 @@ cd "$(dirname "$0")/.."
if [[ -n "$1" && "$1" != '--'* ]]; then
URL="$1"
shift
+elif [ -n "$STAINLESS_OPENAPI_SPEC_URL" ]; then
+ URL="$STAINLESS_OPENAPI_SPEC_URL"
else
- URL="$(grep 'openapi_spec_url' .stats.yml | cut -d' ' -f2)"
-fi
-
-# Check if the URL is empty
-if [ -z "$URL" ]; then
- echo "Error: No OpenAPI spec path/url provided or found in .stats.yml"
- exit 1
+ # Embedded OpenAPI spec (base64-encoded, gzipped)
+ EMBEDDED_SPEC="H4sIAAAAAAAAE+29y3YbSbYo9it5eY6XSrcB8C2JrHXshkhKxWpRZBGk1PWQqQQyAWQxkYnKB0lUHXrkuaeeeHnite70ju7y/Nwv8Q/Yn+D9iIiMfABIgJREidGnT4sA4rljx37H3n+thGM3sMfeyu7KZmu9tbbSWPGCfriy+9dK4iW+C9+/dN2xG1l7vucGidU+OYQ2V24Ue2EAv2631qiX48a9yBsn/G2pj+XFFn/5JLb8sGf71g9nZyeWHTjWe7fbCXuXLjVsWLEbXbmO1Z2IDta+G18m4Zjaiq862CZqWceBa9lpMoRZvJ6Nk1t9P7ympiH81hvaiRUP7bFrXYfRpWX3ojCOrffwddwejxuWd+TGsT1wG9aZ67uDyB41rI5v9y4b1pGdRN5Nw9r34l4YOdDi2ksSN1r9JzTxBoHtN2ieURi5rd+C3wKxNthpN/X8BBZgwcrEOFacQGMbx4lD+O/ItTwHl933AJY0UOT1hlbi3iRWGqtuvTC4wmZhELesj6Ob3u7q6kdq/pHAKL85P30TyxEn1oh3Bf86nt2yxCZ5cFhf5CZpFACQ7TibFYb/exSGo4/Qyw5iywUITxCIXmDZ1iAK0zHBk/b6L/9i/ZR6vUvYVZT8Fqy3GEzQhfZMZxhZALCh9fH1wZm1erW+ioj1sWWdwe+V+EH7aYaBP7HSwIcl01AAi8gawkIBT7s+rDlyR2ECx97rQRNYzEbLamco4PKkdiAaWEl46QJWROGoiE9hlEenBn4TpYF1jONt0Khh5P3JeLUXOmLsk3/sHcAKHbFXam0l9gDWstmy9mzfz/YMiwjTIIk/wjoAKi6eZ+D2EtgH4ab8vYFDBVk//BE69UOeIw0QTxw4im54Iw9A3Zvfgj0eFSf5iN2voS+tlQ7DHiHE7QgXTNDwGDPTMSA87Cpy/0jdGBDgHPDO1u5jj88nwZX2AKSxC3iHPfOgGbowCtzGdj/Jnz+2j62PEfw++djg7h//+m0lmYzd31Z2f1uJ064iG3Erhq2sNH5bwc0f7sfQ4tffVv7zbysf4Dt7PIbP0BcwLsHOSZS6t7cE18jtud6VuO3p2IEGfKNgQEBa6iG/Z/wT64vcMewwFniaXwy3dz6Kk4kT2MUINkNXIB3DCPwrf+EA9RCfxd3LtZHfZc3oBsOmWrQ6NRud7b7re3j58FLYSXMUxglci55La49c/DoIafH2BKkM7J/PXwwbu398xEaI2uIX2FLLOnX7bgK3/cqzBfWlA7MtB++uwiAaruf5ruVEXh/Q4jDwEg8otg4gAVWYxR2Nk8n3RfAB6D/yEuEWWuPIvfLCNOZe0Fgc7EdEqyBMrC7ejFHXQ6JEpxGP3R7gfI8PFbBBYv1eRhB/C5r4C226l0ZxiIQU4BOObUBoPDIvGGDHpnXmwREk9mjMLQ47x9aLZ2vr4pLAj38CpWtY7k3PHSfiSsON852YsR8p8nmAZByB4zBsT1/t0eAn9sAL+Cp4iCG0lI/W2IcNf3S8iOH/b10XrrP77wT0j9SxQ/eDqTEcw0fgxQ4sWZDrw/2P38OPcegDbntJgZoShVj9i+/K7arAMPhmJHvD5QDycW1DV6QjZcS0FL1WV56WdUxnCAcuIDACNIMDCkfI/+B88D6kwWUQXgNSvU19HwmzdR15gBCiC8LrYwA/fUQeA6TDvYGr1vOQpgAlgiNyI4IYTXhA2A5bHcOpAibYUYTX8uM/m0ygm4JkN9+x3PEReSgTp5gIU+IC0b0eIiuDO2UJ8QTQLnTSHtLNpAUyCsB9FB/3kdp7PRRthkkyjoF9Xl9ft7o0UwuwcJXaQXs46cTuJSgMBUBDlVwDP7kj2/NxBNcf/z3rCr+kkT915JXbxgpAwYVNaoOeROEY9gucdDKnv1jZLQzDFCwGEvmX1gV6EBcdAsnY3djcfL5ZQzTjoVZuP+CogLxeMqFhu8Qy8CrAxw/4MzA5nlGsHESo0vhncEw+yA4+8Da6pbuAfb00LnDfhvV7OhojubHpiivmK1ldDLPjcbLERtjO4pZA9BYAUy2EGXhpLfw1kzdCMaIU9tjuej7sEsZKbKC7NnM+KaNYfFGLgiUxk9ysbcG8S/Pu6axdY/mw22SoJOHyGbSsA5v2TD2RfMKdICRHOSwGodN1msC8Wc5LiPjbAiJAN6zATVDMzS3yZeQ5AK7SGttXgMF0cbvcoiH+ADkMBoOP9C+sLMa7FBMNkVyCxFXJdUjkjpn32M6VHeCNE0vJoO0VjmyPL1d5YSdw3yUExA3kk5dD+mF4CSfBjBxlG4BlDJBAQgC3H8kLS2ej1E+8se+qo8gvAFGq8uhgLyBkejgG/Wk7zLdgi4CSKGmIj29A1TiJvBCvTAOmoyaoegDiRCy6RzFjj0I1fQmC0JdXIX8ASS2GwxZ3ZNfyvThpiKvB8hSuD/gcXDjgXo4Hv7KM0RBnBNDLTfkK+Hp5PvwWJCQ/tGFAB+g6/0UyFAk+iF1I61BXQUSQCoadwPkMR4Sj2NqJgL0V7ybcm4oZI5BqxqA9TAixEa1xADfoRZMxXpYMrVmM62fy+nSNUJ+YuHhp4hrCfcvq7P8Djs8RolZiDdwA+RWsSkjkQC4A+OPQg41/D5gH7Ae+HVnM7f0JSSuv4F7swhkM4NRIzhqnXR9FGr73PN9xB/h5iLRmVbZsZII2aT9ANojrcytbrNwVkinILEM7QO1u6GaaXS/bkTYDif9M7XKtc7oSMOwcdHal8gCAIqVQ4LcEAO31LehkQOpxZNKPOhvbz0Aj0MaNUQbwkK6S1Mkf4gsv+B75NAiRuQ2rXVr2wAbcIKEjcF3HdVq53TTkJ1gJsIqxEoMVUK+gGQkkQBx8Qc1Xb5rAWptwmqMmsE/AOoCXY3VDB+mU1emBgMKiIl1/HI/kmxaxSdCW3QgEpP2wFyMjn8dhoV2Kd4SmLvF4B7klCkSxzuodIfKgceYWZZERCEd4z3BCKSrRh5e2c8oaXHkth8GV7XuOVPEsuHBwORKUHBorrLfRGNMkLfztXyO3D0P9y2q2hlXRdXVqv9tblp+gNY6hg/73mMeNe3Bj7Gkz8K/x6kEUhSCc3OKA54FCC4e/L21Yxzi8vSMP2BfQELi2HsPia9z4qzDqeg5w/Tq7dkKXqdbQvpL3/I8ULhsQc8TrrxEAQF5eAQt3yls/BQUpjXou7bhPbb7C/YE01IdhKm6wuNooTVCLODPsgKweuSQnChCQWPI1bv88ABaAKIwC6QHJtmVI5BpZQgJuWnSpmY27NORXuP+zMDyyg4k46wqeAg1ADQ8mkpDHsHEUSEAiBK2c7BbIGr/GvR8GzExZgptC4GQjabn7ak8amPVrOLdruwrBxyxsgwiPliAQSiaaiBZIBqYMJV/f/m9rr7hSpFJyf7YU3dZz5aH1jFVsCSO0+2SrRMMzDMeWQTTj3NijMXm5tsiPdcumFTaCdLAbi1i6KUSNgvKbHB0/c6MKg4tmec/bFPICshV2E5ssoGigFXoKCdPk00L1G7AjITdAE03H1qnk66iVoA6CwqYyrZEatkJS8Ia2bPFFfb2IZs88HzgXrxSBzZvOKRAxKRjkwTvaOxE3NpZCvfLTCPsdHhAZEAjH9Nlx8tKX5zNsXUUFCU1+uKAafagdHSYK/ixi2w6xX1tTj5T1qcEWKWH0yOxApCRAt/dkDM36kUKXdUZNXftIanp+eGlp0KYBNTizS8jrJO4YrliRTnnQ3d9BGYJFwdUAnEALDP4q5qi6DnmMoPHkklgHcWp0O7Jh5YHbRAAyp6ZxqPMtNgbU8fm0g8lxn+yJs1cMInzKf4l2dhTZaCsFGI/i+Tuut+53mRxBE/KKdRoh6O8FjQNbqQ3JH1Lg3RlAtB+tsE+i3FVhchx+bOfITWnTJfCp2W+aMd5P9KE2r+zIs4PkLdtEyDxPRhLRJ0hHXSJa0/sgwt2A3ntbstTBAi0yARDZDWknkknRTEQ2hLdxbE/QqqRD9NcVULhxT+zFWPmAGC2VFfxZHIEEtIBJeSVvPJCQAZRFMLKtXB9RfP1hxukzvko8nQWZbLoL2VwDrcJG24HrTh6VkxxeBsJ1ws7M/ERwuMLHWTLcqtHUzQrQfz9zpbINmccK0xaGB00ziexoIiEgD86KU2S7TNgzJ+/KlHXPWo0t56hYz23paEoblgvr09/ddIBWwtJJZ0hDWIRI03GTju6lPCpd4CkUhH+tkB/g+uBcJd8nTSdkdUDsUtdbifN1yFrWx3H7duon5I9poEQ2f+msE2bNumHou3ZQAhe3+zANW/u2H7vFPjTknC6NlfcdZKKTewM2hRMQgK8yebFIzkTj9Q9LQfrBADfbY7aNGgDPYfk5xxZ8ImQXkQufBOEf0DEsAHySm+4N2qzp1oDuFCFHjnP49l37zeH+xd7x0VH77T78Ir85af/85riN37w9PrvonL/s7J0evjzgJmcHp2/bby4OTk+PT2kVU4WeaYDLs/Aa8NsPR8A2DjDK496gmIvaEccpo3Gy1ektCiE7tPfY/UObBOQed0DEBmRyb4QTrUGjpAKTQcUJo5ENhHsFb0sTI06yK1F5np6z4E0BcEXeYmJyPcGEFY2Ko0Vw0H7VRnjZNc64PR538DLeN20qRVR9mnOzxfIXJ02lFaNPK26Sr52CbynUyvuTG/GPFALRRC8pfNuMhT+VfwP2gGFS0rPEX8ZuL3LpXPhzn5yt8STorTQk+yw5voUxuZ4zlqO/KlyyxaBKskGMKJZ1PrDQ+Vd1HUpxHSoSQsxFXsPD/RbLSBjdU2cYuWXuIfsPQ9gsi7ZzRxDTZ10wDlf4iDF4jw0DrRJtFBvVFpubt3Q42YZlZISQgVvWsYjKgi89X52eh2EnqGSRyxKwbcOtgawK+1yngpsWF/V+6Ao/8DQswbBdihB2HVoF42UnCaP8ZZ83A1nkXOvSncBw1JlCI2UEC41Nl6TDd2SBofVzoggSulCg4ERpjFviA2JTGt+1hWBDwwv8gr/kqIghRbCJSeTdnW/HsTFW4B/uZKHlqGCYLIgIwVoCZ+z6fQHNxacQG6bdVg+PF2Dp4TlGYPrgI3cQ+qOXdu8yHS8+eulcrC6NVD0XRkFhTMRi89iW7Fc1alGRVQddOpYSIMubzy+xRFkOKi4vLElbEL9jUGhMu4YWPMMdyISCawmmxK46wK32w2ARMoGnR30r5kLmV6KKQJ9wG65zPj5d6hwVNqJ31otGGOOHsX/wPZAq+4o+u16UO+4i3ryWwUXtpEqTLTpBvRsKWKZoZiIkFBrDbn4Np66B9gp/QRmldEJfJMsFUqqRvYw46RhQPK+pYK2Fe5l4kepEVwg48zmYM59pv9NGBKaPhsuxHWOsnaVPJuJXLTQ58lLGaTQO45nSnJTj+K6Ut/x+OOHLlJvJvfFijppbUeHis2bxAkBj/ipMk0GIf1ZMVWZBghlTrC0/muAwM301TP7nia1CGSWsYIkSOtnxhfz7j+gixth+auAwXvQwNNT36Suh1k4TyQSjbIp156DF8j0T+mQYLqInZev/I+IFVxiTc/ghpiDjZxhlEYDEzO0gZiSh1Sgi1ubA52WWhR6jcaJgRYBsCeLSUfCEdbfoKMXfosG8vYiAbCZRGNsmLPXikZEMJ8ngS3F+5zWF8mPCNl0gx+hOr+fBBcKbXbhe2QT7fFfu5W7zIvICPYeDLtTT8WJ6XINdG5bXt/ixQ5mOOmUUzg00HwR/1HDZYeDw/B38dMpRn9JeD3RN7gOPuDBzYSc0RWkvMGTu6skIZro5c5ftjsLfvUp0L/ABbAeUHyhKgGJjSFyaXvIEVhdwJCeFO27PG9l+jXHfEgutOXABHmLtZZ5FawVwMn8WA3qxAE4VpCWxuy8/ZY5zKN+qkCrsuJp1FKQIuJ7Nvt1DpLwqjydtXyWoCGOemKbsRCsPJfRUukQFKh6Ox5WyCVI4yWp1hii5Ukb3K2jubNtT6bFKTsvLLxDOZhDhE9MpZmShVC9lTdYsZ8qUVMNqdij4vvFbfR6/1bEQrjR4141ZME6suzmxjJPqEzqpjE9qIZ+UcT59Q84n42EyHibjYTIeJuNhMh4m42EyHibjYTIeJuNhMh4m42EyHibjYTIeJuNhMh6mR+phuqtpLIeVn9Q6ZkQ2I7IZkc2IbEZkMyKbEdmMyPYARLay3JUTUj6r7IUp8q8+jew1Z9dztokLwqd/aII7pWyXnKpGJrI5lZmN5l6LqKJ3pVlVAAh9ySXUPAwcykUTK5sq8yjyaRJlIPOmZUsOptxt+ewGwGox+aJI1xDz7qYGRpRcb2SQlBkaWfbEdJVkdaUVeJTVyNW8fY0V2J5zRllr5k6gQ1qkGlJppVSZDeU0QfbWSQcDWMxiAktJzhCjoOtQjBsruSJwr3Pb6YXjyfxTFzVJpl+DvWEIFMKahGmkJi2TpDMcJiuuINoB9XHHs49WNj3BtPbD0HeqXbhyNedTV6ANUF4HpU6Zg2Npd+QlMyERYlqWtGLuDvW1ummSAD4AoXT92ZNx1vEZc72cUBISmA6+byD0LRvIpCtzwpzhAJifhQpMUMYklJ1A6Bdpa08i78ruTayT0Pd6k1bFidEI2HUsmgZh4jH7jofhtcrZLf2sdG2Q007fWJGUEW5Vn7ECuATGYhxlD5Bbl7ycFAFYsdjimiqpXInC6MRA3KR565PFfWoR41mpXKo6rmhUfk94w+pT9q8qlIMzfNWkw3kmksuZhrncBEXGSh9EE/paimysaqAS0H+xABJ9t8VwkgWuQzvDe4oQsEUun+kbFknoTTCUCYYywVAmGGr28CYYygRD5ecxwVAmGMoEQ5lgKONZM54141kznjXjWTOeNeNZ+1o8awsEQ03jb2RNWsDSwiXKRI8qFYEMLJldalFza8kqOsvE2knJ7KZbVWW/4zQZp1ydo+a79K/KNmSsq8a6aqyrxrpqrKvGumqsq8a6aqyrxrpqrKvGumqsq8a6aqyrxrpqrKvGumqsq8a6ej/WVfPkwDw5ME8OzJMDeWLmycHX9ORAf1pG6Q7qvzgw/g/j/zD+D+P/MP4P4/8w/g/j/zD+D+P/MP4P4/8w/g/j/zD+D+P/MP4P4/+4R/9HxRpqOTqEgQuH6AT2OB6GyXw0NCYrY7IyJitjsjImK2OyMiYrY7IyJitjsjImK2OyMiYrY7IyJitjsjImq2/aZMU2o3dGhjEyjJFhjAxjZBgjwxgZxsgwX0CGaaxotphTN3aTRYp9VBnHpmGse503d1kdjNb3Es79Q28B4kvNkBuSpQwatEwyHONZM54141kznjXjWTOeNeNZM54141kzViljlTJWKWOVMlYpY5UyVqlvzSq1vGftnoLBRTKcXM6VoGjBqkjSkAlfdfPkwBCUHEE/yvomOCOsGWHNCGtGWDPCmhHWjLBmhLUv40I0zjnjnDPOOeOcM84545wzzjnjnDPOOeOcM/YeY+8x9h5j7zH2HmPvMfaeb8ne88WdcxVrqOtq2wNiX8sYgkwBKKZ1GIzTxIpAsHBBMPfj0GKmRDzCD3u2b/W4rdWPwhHzQi+Adfo+Q44EUk2kB0jd2Ch2wzT/6e2e86d/aB/9/sv56Gp0NfnhfLfLRRjgKiEOi7KxddYsK8yiEIergOMK0wgOsuv6YTDAMhZ0LQDqcVjDnHM8toFUAAEF+PNWetTze8sJMek+bnMM2NiiZP5JOGAJ69oDSvJEIeqT/I7Xn29sb73YWd/Y3Np+9u+9jZ3kl3M/6W04P/383kEOryeYz6ZuCtpA6oiSPFcC9ya54FVdAOGxR4QZ+3WkRnLwwmcuF1BRlyHbttoLMnJHbJBnfbJrPeERnlh9N+kNAUu4lgNAP/UTuOpPaKbs58C9zn5uWftu38a/kB6roQhnwsCfqHlQWwMYXHmO6+QhKjaQ4crLyHMG9yRB8FjCOgJqT9SSMs2eH6aONJ9gSYfEJfgIV3lyHUaXFg5ufUf2HV6vxcZPZBnAjXth5AzCp9/DVqVsqwbEoWxxw7q8CkDs3Fj0Y/Marl8MFORpy2orLT0zhkpT7NVWa6P1/MX23+gO8K5r7h4bNUDm7Q0tUCN5B41s/fCTDwoe/iHX0rAS13cHgJAN3GpyjVbJaJEFirOeWVSkh0cgzCDNYSj0D4IKHjbQH4DWqBk7l5X6UOTm7X1RGsS7BfsGrj5bKp15/V1UsTLai7a90sIEzIGp2UqE0Ax1tWdHSsKktKmIbzMGXdHNLNWlbgh5lAbu5/J0ElpmwSrPpU9i2tQ4AuFNmiiGVI2nD+cSkChiW9AtFkKTKmEzd9Yf0pEdNFHzpNmHwFARVn3NhvWd2xq0gDL9HTSenvsEbs4RiMVddW+bSNeR9xM3RlI/8MMucLSJlQYeMAVG0SEosixV1pOxngAJwz5SWoTzO2itP9vCM4ZbpZb1t/Wt9W34z8az5+u4ODTq4wbSQCoecAdGsJk6wjQ0s0AmQNklU12st7inFDhGkLhA0qVRhw1uqe+/rQXqfU0xwiPF+jMBbqvney4SMbmjNsLZOmC6JeENuOinoKGwcE1Te6PB+embGqz+Cq4GAHCExjx0sujH27LO8AKP+EjRuDoKE2rWgGvleDb/SbeL/oIbxnS27/k8nAMYGziIhCEb01iSUHgCijvo/4i50Fe3jQKG5AkICgvXAWlgcIFG4wRawGKdNKIeNlX5QuaGvjlxAKjZh8kRi9017I8gtxKCi4vG3S226iWucihwDSIv5psojqYLO790nYZiWYAoAHNi9wSZILT2j8js9RQBKyycCNw48XxfmVQnYcpHGHeAHC+w6mxJkTuG2QlxiOKl8L/AdrEumeK0cIcIx4S7op4Sfh4XvB0FiVAIDvOpnl1fGiUJWs4HF9AlFVxUGGOJkcxGIQvXsmkTmOqYtksaTEtemlhdFyFBZN7W1aMiR6YfbYv4oxAeFJNunR20j5rnnYNTNhfDmcdju+fKifVOIL0hAczLGRdd+6LValFvXTzhG0wmzzrwEXyO2pP2UHCTNpghCHp1DFSzq/dA5xxW2gJR2RqB/OihQNTTYM7L6SpZ8F+BtcC0/7KKOjoQYWixGoNAOrLj1bzgiEYivgx1mU0/AnLnwM2Wl4iLlxU5t3TOKhO9bI4XQKPtqeDAs5ZMXFqYZtOZRkSAUgC4TLKR+JsboMMFKJ7Pn+RPYXChrhM7Juh7/UsbdhEUWxLyZTPBRyRwFZdRKr3ZhMLAX+Uplxs8A0Vn4dOQt08Mn8AYgr6O7a7ne6qWXuHOV6vQf60EwBhxU1w1La+q2cFEVYnLL+oUffEjRs5VSS31JWT7VgAsXIIZ7sCMIilkFwikcKN0Au3cRSkRJwTRazcRlyI+TpNxqgO/ZL6ucbVWSmDR6WPMHrNBGqE3Iu9gtA6Z7imsONxvSFIghWRgVGM+sNwNZOpFrCXjF5rq+GuOoGcFPhUNy77K6AgKvtn3AiTqc6a2CO1EJyZC7F7JbjgN9vcuHBOIqbrlQxd4Vyp/FwKg/PHvYlfi10yGW3nJDaz//l//47/8x3/7cyVj0bKCoiAhGqXAqqM6aBRnyUFH/7YAIP0nASP9q9lg2ueWRThtbKxv76w9f7Gx+WJrbX1zc32rEkxeL/xzLgSEnLmCEt/u6iqR03hVjLD6xuuicLfaHo99YQD8HzbWOuz0WeVTRIoUr5I8uUpssJUdjxzo4veR/fb6j59Ozg/PRu6zd++2jzv/HP6w+cvP45G3cz6xf/5pa7Ozcd7e+ePy8s/J+vpPOz+et72rF2c/j44vw5+ipQ6rzK4Prn7ef/ky/qV7+lN7snl+3Hn/x+u1N+nZu8te7kDnNi2ccn6m7Ki1b7STZsU8d9LvsWGbGuaOeu5CcrrXyt9QXVpbh/9bW/8SR4+2hzwsVi8Gly/b8J+X6zdx0P/pH0dnvevjs8s/j/fPt4/aud/qHfKHHFUuEWUjsBqB1QisRmB9rALrJzD6S1sl+TvEXYZzGoeevKz1rOjnAfA/f4LWpO49G9Q/n6VcSerq7MVeojSoYzTnaCYyF9azLRauhpiNq5NLM6N2/rUJ4FtBwgbAxcZ4JuTTIv4j4355o8RIPKCAfNbI365cQXj3CuYiwAh3QCg88gJvhEBfmxJ/EPYtHqoCpJK8VsRYioij+EiwDqmdLRp9LOCI3GiI6cUUK6peFAAbIS4W1qpHwNXtz6g04xtCE2PCgCWQL0RYUT1/cpEGeq/pxHjaZkS4kk/mWTpSiu+gyenlao7L3oFGa7QZb25viDgkUF1iEHQAmjlCpuHbwSDl6ImGpH0zY8GWU6aLdLh0T4mUxdmJSmc5D41k6PXBmbV6tb6qAaqOAyt/rSvvyQzsVeikAadM/pWBXdJP5AZCoKID5ghG8aEsVL3xYiErla0Z0/hEITZvoXOSclkJgNR57v6Y9nCocInDxtI+w5PU3o/he4bvGb5n+J7he4+H7526cehfuYcqemlvjiJZiEClByMXzqjispHlJLxMxxdkipvZohirUG5REd2hNQJVddYsZFUBhd8HLl/dInbtqDes+q0Y0ap2XNheYS/lheurLCxJzV864OxgLB5vipotUHmFYvuQLcJ4cP3OQEvPjnQy/0Dx16UeIuD2MIqPHnSoDzFQhpUPCEO0F0R1RpYMAzuFSBWBFCaTC+pfSdLFCtBQ8SFvwAAY43mTCaNwjLzPErh12DWlYYOmzuDO77FeI5N85bm+swh4bd8PrysfhObWV/UrsM8L3w0GybAKWPhm+mbm77GboJFsLiWdhT4lS43YTgmOBB2RGwhYdh/hVAnBM1jnYiQHf73IzVZEytvsCcesjVaeYCGObJn+NkUeLds7Ccdeb9nOjjy7ZQdQz08EK15yDJdFwMV7V9zS3FnPQ7QSccRYOpJnSbMgvDtB/oyx8IAri6FexMzyIov1nbfL2ewVFkNru5DrX8RyOxe4pat1S+FzQNEuxqBDXXgoYlayw8IpVGy7tPCyZOJc4Ys0R0nupaORIqHga6hUDHKnyYo5ukZeAZm5HwUWR9LfcJItWuUws/MOGfFmYoFHYblwR6kn9GFOYfXO4tOLgurcSNkhHJbl3sDwIuY+e8obiqhykAOq56oRALdXJS/iaCJwQvjfNDkDTSbqdD6P1SRDhmmGE2Xnp6bzl7Osb0/zxC3QW/RTDo4FJK050TEk93PMogri1HE553rUHXLAwo/7FOhSwzX3V8nrwvHXqJYT4oD+StpIMoSbPBgq3/A720/RaBXhU5s4abr9PqIz3nnKoYFxst2Jpkx/uJsfkNLYXMjH1fDvwHUuwhTPX3glV7IZllJ284Si4I3DeOIrt4Pe56WEaaGyixcfyAR8X3yoEFq1hwr5VQ3DFMSurmvxeogSFK6NxH8NmTXFM7eP0rxZkhmVGiRHJJb2cGfGWLrFn9Miy2RjBnU5JZBojesubjqxmBVigNSi3yccby9DNWpdI4EeKzPUJxgGseALIW3+MDRMzdBX4Syv84MUHeh1IsmZ9yRAkCrDEgRqt5SUBC2unEwEfowtHn4BO3gnBQJFD0s9ekyZt4IjWWmQuALE0SErN9kYODjFBqpfd9nUmFaOhYWIT3C0PpvLfbvnDult3vwBT7LGbPGTo6pIEFb64HxJgefIf04jQtxg/hSH3FoMdIWdCqIN8gwt6IRTBIg4zxrm3+x9gczKAvQJWl5JYYomlsuvEKLgqz2QXj334SFYxiJxffDF0LXZKEm+jQuRF6vqdRlIc/gKXiTOEi9klaWJzmEBkZgB1LB4fu0pjZwAX3aRRdr3eVPVgKbb3ElcSiEEtFtILfONBbOgg6d+QXduDvGDeaeQZ3zJHKWlDB41HtLrPTP44lQiqxGe9Z1EZY34IVTtJAEBDY3vM4etFxNUpauvKEip5X9A019Cz3ApMQiuKDvK/OAgBnp2kIjATNWcBM+7nTPfgvjBHHJXXTJx2GlUg4jjuzO4KbDxQCmXinrjH+2BG9QQZTN2Q1TPxl5qQLU0Tfm9GyrqJBKxBpRYEmheeUCmYU+n7sC9mb9qaJb6Nr5txjdguDbWepRZIhYkEoP8EVScoFxkTeAnkTdJZPeSHzvzZzuWzwVEH4rpo98JUgJKza6NDjWZmWHo+qgQtawztCgg7waGEnKSAP5Nkc85twfxoeoOMSzjejdINL6H+yMcWRegsF1c294DpJZihffJHP6IZm+zOsvOlJOV+XLkQf50uhc6rEEIo9HMo+Tm93CS9JR29r7IRlj5yHbK3lQHbX+U96b29qj1PewuCJOhSG62gCMov26Q9hZY9xjN1h/mXWeJndpEYvh24LyHC1XvQuf73Atf5OyGn+k+7/F0SEv1jlXGUTt7cFvT7+9Li199LX82KojVunWJrQDlbdlQAn05Yxo9epc6LqlLYaRSTFqYq6UipF5wYj0bZcNSufFIou6DOigA1z45PLhL+ih0JZbeletGSMrPtIinYjl5UqSOkmvJVIC6mbR9rXUdG+2ZerFf5QS4q833To9I8hb5uGTMXfYNT83h51uQUCaAby5gNKFONXLfSfk7/62jKKyiQ/mkj4zWdPbCuiQVwLmqDzX8THRES9g2cw55MyuNsBmmVtliydRAkNEvwf3ZHmfck3qWeHVq9Vlv0Y6nQ0DbdscNnE5xAVM9tPXy2R3ZGJ3mZjlgENMwZTgP0LJOOM2EToQDR9r58TmP7NGaSTDnZJ7RBxVUHBeRpdOrA/rO+d7eQacD35wcvN0/fPsa/nrVPnxzcXpwdnrYfvnmQH5xcnB61H578PZsRjZ/sRg2iqlkyLhOfCIF4g5bnWRC4aqFca4a+AZkXreJTcu0NpeQuAhf98rFl1J6Cq+NtY3t5tqL5ub62cbm7uba7vpGa3tj7RdmSb535QJSnYX0UvMOTrVzzkAU53aPmZHVHKCAN2QiIpHgtcIIiXE3ETDCIiee6ja2B0EYJ14vlwVR5stztSR/0p0OGDSmhFn7nGmOhAlOw+ZPhEs4mpKLkOzk2RGWkOFIP/biQzYBlSlA0PzDZ5jutsLBPU1UndJ8GrcWzbt4JNLv5gYAoxrXMLc0i3sJazrHpNY4MuBWPZEaOXZ9/lsLaS3KlrndqZWWwyaUic7KdcmnLyI5T7W8H7uzNnMW6NFAV4TXo7xWdmCNbnq7q6toXJHPfd0seRGlRpKvABADB26i8tPRj5giqJbRWvpo6S02/C+qBZgUwE4dL5wNNBnlE0e9WnmiTtKuD9cO7UX5JE+4WNwFJSUUliX/c+V3GsHlPKvlOjo6PDrgJIIydZdKqUVq+eo4GDx5Kqx4vlvvhcFx5A3IjIZdKJalhH74S8f7Ux9sSgb6VwjNGJqiE6M7wSyhWoJkfNn+2lssG5Rr2RqyIjN+ffhKppYCIno5JVK0/ngxjyLGfBfCMb4NcwnOlxn1CsdBas0gRAwoELspINwXLS3SR0CkcmLrO7oMq3Q1+HxLFHSWMJqnzuj9w7cv0eFoUOPORIDY7rWWXI2yGONKtD3DEnlMYGOAQk8/09WJ80g5hSJee04uUFYA/hb9ZN5gWFFY4bYkLJx4N64PPBc2G5NVMiwe+65F86zymHh645sptl/Sb73gUsB2/g5qeQxE/CE+1wkuiW6Ti1Rc71oEUtECPGclDgvVDUam3HqU2A/kgEDWCmBJRKYLtOEw6zD1V9ywIl/fZ0Edbznk/zIr7Xw+PM/tOMN3oQmQsWwe1BC1YXs8EPURb7dGmN1zwf6iV/kmsdeEl1SSEnJDiMwjIlWxnVO5Tl27mBv5DnKVHI0eTb6n1AeWFmZNMQcYOJdYo5AiYtBlBMp0g+7Z4T6VgRq6uT7wLVwTzHoYUOZD2gRXdBGTXbqT7y2qlnDtgaDmJRaAyfarhhIRa9wxX85mmvhcmGkXZUNyLFjf/X//5//+vz5twB5LiVKhIdkqbaYVVHrA9y5d67eVeORhyg509bi/rSyU51P4RPXNP4kZVT/TXdSgWccAJ1RMySz0s7gehrwJMTL7ahaTObSDJKFDHMyUJ2v6sRd3ghxJaIId1w1eTk6ynxcx/9rBJO+Oy7Yx3ZqePXxZyr5w2Dm2XjxbW88qH9U0J8xaU6Yx35YLp/BDBLTscKWcsaiLgmjP6nHx1s3c/uRCa76SP4lvFaiN2RLrVFwsO1xOy8cwxX6RV2rKCXXvQPilFYXPWsvvv7m1ubOzSe8Sv7ZaCws4GgoPf6nkQmZTK6TXRUtTvapBr1K0RSAvo3TUFvfMUoeLR+zNLhUUI1teGqdkwJDZxvQ0VSAP9L0bCkTP6mnmofL3Sy8Gpty1B4i4u/nMYS2V3S3bRj0dW4nmYgd6CaF7sbFK/Fv0tuI+gD/XkgTaItt5w8IuxCMxnE++MscvtWqV+vQvNtafb22tPd+pYw86O/jnGXx8e3x2uIfW7MOj9mv8993h/sEx/nvM37fP9w/x86tDMnp3oPk/Dk7hrzfHe+2zw+O38OfpQXuP/pxq9sQIdWVLQjtXP/WJfsARoe8qBXxAU6xknrFMGi53yinmmO6QLTvm2xiJCCZ+Ia8X8UxqPUE49XrDpt7Z6oYOCSgiKzZLDcCGXefs3iz0SufT7eE8iV7qqi5uYSLDwFncQpNL9Z1yhFqQ6AtTlCTzFs3iJ5pfiZb1g+c4brDgsiRMgMwOqT+fdIAA9uWdFmakfRddnHWqwVbNgLVxgRgH6IpwpYRYM6JzplcysyOXhJq2ZszJa0x5VooacCC3eR7QE/Cl4ZhS/yzVYOnglVwfcqQEW+5Bu3MdcYXrMJJMBFfvvIlZxlQPYExOlGxnUxj4iJMGVgG/GPVQ1wV1xENKJAcG1rD+HoUhp7fBUTlNpTuwexMtcz+yfSHfM4GhcoowhQLP8rHEml2qtF5dra6NJYp0LrskpaOX1iN/iaVaWDV/LMTomvJmtfIkpLdGLmGhkmR0Bp5x1A8aIsmkxVvrmzsbWpvdla3t7fXn62tbOzsvtrKJQEATiZAu1td2Xjx/tr21uSH/yqd+npaZOZOzVv4uxyonTc6PpQs1K/8gWch6ycJQbpNE9p8114Dyb59trO1uENnf2nmOCoA4Y8HImd2RHeG4DffbQhmMlPCxH4K61LL2YJJJmKrq2lQkmkvRYIzK/7SiMxJROU2jsOKb7Hr+mu13c/P51vb6i52NtfXn21vbG+vP9P1+0EkY10SDsy+ovgs+mpyjS8RtZ+Qtxniw1JWmTiJvsHEQLJdz5QFtoOS6HHMGyCMo8wlXLLnrTMSFvODKI79vyl+iD/r30EO6NXHlhEIYfxlWPTOvPadW9AOwAvhBOCJeoFzhKrXqbVkVFIlBtfEw/zDId05skSmGCBW1GbloAAXZbqzSxnNClcjuJ/ftbqVBcx6irIBYHZEY/W/wceD1VzilbQRU92KKg7Q0GYu2rw9fkaQqu1vUnV7pjtKElSb3Bgh6jPmvsE98Z+/kp/FEntg5K/c0I7Ly7saTGLgM+3iloOEUgHTPbk7hUqwjmggfpoYSUkwqrrHsraQjlP7CDvsK5y++TQfvVLgZ83t4QL61WUm4KFoOrjThfdWCi8JZIRRlQW2MT4V0MRuLSiZpFLDNT0vDVy2q3ynNR5EwlaBYvPdxZo3UcIhcAEWfJIJgapQYb5jXrqV6BohnQiqpjUEo2qKHULCFLN+LyJn/BnRk3ZeZpQwUxK650WiuN9Ya642N0pKaG7sA8d/J2diwmuu7MCEVim1Ya7ugS6g63A0LfiM2QHRN+x5G6Ke57woBNotliZHU8d6OeQrASqfdUSXHFdmlsEL6NMbwvUh/OkNRLkr3IYZw5MUxWja0/tl77IQynzmM4r9n3t2eXSvgYPouRvbNHo/xZlr+qnL85g3mkrTE3BYnvhLZXPg7zjaRP1KYqUDM503QL1F1OdD7ApWbNxJ7T1WcBBFJighASqdG/aFIIRccVos0yI27X441mTcyCwNq5ArmQIOj3ncc5Oqx15LwCLIkGqAHtOuyOQeQCwdsogeUrWpl2pRdsDKBqsr/pJG6HLqvCC6BRqAFk5A5xSRp87v4ksotc0mKAeP0bVnMm569jo2RhQxsWaayL750fpbANRsr11qMM/jCS6XlVK+0Oq3TF16v9tUqpZirWnuJo1BXbpiOnVx7T1BCbLLY3WHjc5JXRD8zg4yU7JatxuoDf0tB08yEJPENp/PKFVbuhj5avwMfNN4LDGVoWPi/F1QssxVPgsS+uRgCLfaRHsPoT7OHXSOgjagw63MLPkXyvHOF245nmnjvBK8pks0MeE0hoXoaciwjzOQcX3xENqYCb2Sg5II+1igeUCMA5Th2Uydsym/yNbBbxNca8AdxHxQrBy1OB94axb3Nja3tFoVTPs2klvzK5gsuwBYx9dPCQocQNkAjEWb+sj8HvYLYS3MoL4fA49BfmjwA0RxKU/xy/ckOvnx3dBLdrTeAfHpC8erTCVQycRwAveSUo1RJA7qzSEzRHiwiEko0w/8nnMVpGkWxiL00d6Fc2P/Oi+NhmvBnE0aOJjD/lKW+CqOjRYQ46qVdu5jjqaa7bUgHzIvj3PMUeJgb17CrcazDey8Z3vleZUO9TJNkee2lwn3DBxDxpsoMM9KiC5e9lTzC8jdDuV/U7QgxLBGpqJ+/IeL30zr+mjmOrbaY6hzrLzpZpFpc5Iwwp4gm1NaJhRZ4AFY60zgJR5Q2oGpt85CXu1fOkkfRJQT9ZLJk1j/KV3Kqpc/ucPbs6WxZX50Q9DMr7eIZt6edWMU0V5zQEfP4Ybk1SU6KJxlMRHsvzh0fem4pd0IRpvMOjpJxiTEZ/jqnFw5h9sGP8uEcVfJtnXTf2r2Fa0AhZYtgmh4RWIVgMAxM/jZEOy/LDIuMzr3R+ZJ1r5gEc8t7V4tQd9FjNQ3EXxXU244u24t7+7EbrhmthcrZXxqceRIqF8uwJB57EVaUzQd88EBwyrtNLflteTLNGdUuka5pT7jtO3AMdtYt3//S610u35sy6t2F3V2Fl+7hnbZQ9i9o/kWtJknp1pPyO3fWSiPSFHU6lwe7+HxW1htSqvX9OBvPKUhP9ycJd4rIvRqFcax8FYtFhFL8497UONaio6XKIScjVtVLAZnAWA9cXTTPRVuPOxXOiWLUae3KSCLvTpMk3GH+Db9Y06peJ3WRlzEyZThblHLnwvleUg0/OIHMqgxTnWptqgl/kT2/2kBUER89/VFGnXMuOl5FfE5PM/2xNbti5nnucBZdV0Re+mlGO+i8az3htk9ESCe+U9MiN5/QAPzjQIEnFgENKJ0dB/5kEX4n1SMMxwoTZen2ggwxi1yhBj+4U67jk/zbgqmkUXNm5rx81ndUsCrDS6HtfA+0lCLzQJdIu7Gb8MPUoR0fhdFiHgKqrIaBCPgoSgcNv5BScWaYfRq3zFgSJna1J7EwDbbT9KHc+Pm4maKPmcCbbUnOWY1whXHZ3ieJmW/HxP6v8pVjlgvShU3geFyrjKy5deNxWQKrq0tmpdqE5FYZ3b2pxhXRjPGdx+cARxHQNU2sAxylUEkF31NaAsfx1Y1pf4Nw1Oe2RGAe01sZUjBPIODYA0KfkVcIeK4Zh8A925WxCMuFcMu1SJ0JRhsMikx/Zq4VLx55cXwcHAawb1hK+cnMtLvN6YuyJcA1j8ORq9nkKu5edfyBGmNO7EE2VxZ+EAdhWB3FMvswuN85IFzlu+vlzoMHxZSmQGDiBS4tYHuH+t4reoiw+kRbW901TT0rscXCK6zq8xJttdPy4jbrn4uomMSi0OIguoqRjkDzdJ2lwtNpRHr3cDP2gaLju9ARDSfplGCEFdHqYvJ0sdB/mrGs14/SRO3nxAuCpeAypo5ilDfh9UnkhVGeC9UdSgAB68SMxSi6afcAEXtSDkPLyP9swXGGgUaLUauQF3sFl+Q8al3S3EqBZXqQt1QcpJCf1X7UZbg8b62I/P5P8hXZxfr29vbaOvzf2sZu1744uPp5/+XL+Jfu6U/tyeb5cef9H6/X3qRn7y57ranvzwq62Mr2s82dQih4vu/F7Jm0fWK5BujTHo9XsvSY5VBwQYOkKF4UaIVo+qvY/d/1Tc/YFVVMZLkAev1N64V0DtBnWmg6C9yKQako8Y7r92Vsd0MuZjYsZq1PW8JLO0lxDf/9v/7Hf/mP//bn3DVQVrwPmoAsfhdy7EZJSiyE2K+v725vtdbW6I1tTpBbmy7/FF8XCIkGL2Ke3qrFCtqlPkvCo8LjO1S9Ukz0ecq1SJBOc8DEWQlapMugnkQT8laDFppQZrT8q+QxplcjQtsUqyRyOE6jcRgTyaJV3S6lzmROehBJ4tRPYhn0RNmliFPIlaraFr00AsFz8YXC8i4wk+zFmOCD1nYflpXs0XiLK+/cj1g3rRYXynU/5F6+S2UtcFanod+/Pem60MN9gnEPnACdp8MMC/RYwAvQ3lNI87f+fGN768XO+sbm1vazf+9t7CS/nPtJb8P56ef3jr8oJAgKDMaLvko/H7jX9wwNHHEONMifc7/AuOWHQfcXZormxvJ1skm55CdIVvN/lNYiygJMYGCLNwwMKjtV/NNU8rnacw41C2cj91fgnHkuol5PfUWPpEiE3nhxBuR7bWt3Y6f1bGMdKfjUB2Cf6r1U/p1TQwfpZg6k9yyo3EUm0U+npgSRO5y2Y4+sdwCun0eIsDNOB45mY3d9uwUIVHk622sbW2vZ6ewjV4wsuB3P8UUGqjUgO/EdGZEeNeMEpCSgbnRNLFaiZF2kz8uI6xsbO8831mZchkwE3OdvNQlQpHjY46zm7ZPDTAhkM+x8GbDu/cqK8q5wOob7lP3qvQzMLaLLkp7XC/+8ZxGQmY6QAJ9VSoBSBV9/vrv5YndjrQXXtUoCXEiYE7n+7/eqfx5Nh7F4/VMqOxVU48tqO+UFfaPqjkL2Z7tb27vray2Qh0rIvjEb2cUL/LLiUrh1eUEZBDDQoZ/vrD3beUbYlZccUU7MlB9Exs+j+VTLaTR/Xucxms6D03S+dfXlbgL/TEH/P/3j5NVZ8svP79OD02uv++Ls+drG253D/h/eXu9eOcnmziflJK/skedPrLPIvnJ968TnJB2LS04PynqWDbu51GI284t57UZ2BLh1GjqRN3DncxZezlQxans2Z9nZ3VqH/1Ya0jZnGdK2dp6vPdvYeLH1Ym1lKXHr7tI7yMxGeqdFvNje2thc31nbfra1vrOxsf1sbeoi8NXNdRg4kZ1fyBF8b72XP9wJ5eZI7i9217ZRcn+29WIx2y2hHECatTYd5aSQM9taq1AuucZY2ejCGV3AtaScGom4u+Kn2XTyxc72s0o6KQcGMjk4P18//OHt6/BiLz775c147Wr/1fnPlzsH6UEOOc+4y+o/NfSksziCs3GDa1ejj7UF7jq70nBCNa9ACX0Zi2LFQjLuBvlUtytl3PV5aLG9Axdwax4lqpaFy3hckIXdyY9rvcnhs6Oz3vrb33/eePvnweRo/+e1471rz37/k3fsHQ476y9fv/vh1H7/zlk/fffjpv3+cOvdfu/66HcfOOQvN/b529+PRz8O/7n2buP9+eneTwEA2X/VcdZebR+tvzs5X3duTv98eeS8O31vbxx5b/Z+/PPnjZ3JLx2c98edkgheWtXx2dH10Vn7+khbVdcf++/3nctf9n/e/Of6j8Pz19vJuzXnH53343f2+s+T3uYvr2hVm29//uX3l8NfXm97vd8Pt95OtuPuxtthd2997Zf3N/4vmz8Oux3x3evhVW/zp8IK2+nbf67924qmFGCt+9496gULJh4qpsLB4qQhppfmJO12b0ghjMkQ6P1gKCNArXcc3I/+5i5Au+n2+/g2Eb0nKMxfUY20iVbw5kOpehuvmJPaw3k9GjiUvVHRADUcAQCt3JEoGaOSFxklzShpX4WSBr3PA0q+y0Su7qUWa5/fsOjVugfryFkowpzFIjj2JbggQfcTzDUQ2cD5tArJv8lIIxOJCG/1vKkqPeylkpkEOH1j2gSlGrXyPPBAX4W9FPXUumcZp5QJvwbNeT90MZiXg6PGYwqiEr056Q5WM3ed1T6ugIKSCsuUU5UibE/dGKCExQ0Ch2Kk6Bm9HJkmo0Etft1H4Jb75LrytTCxzmMDVSxc6FTyRcN3qqSU+OYpVZTiVVF1cty9JLWoLVuHffmKrSEKFECLWIAnVu1WFnulMaqfF1XtRYZbYVbtH9PRWCTUtLMUAFq0Jsdgid1kgbJntTJpqSnHvo1j4nN6mGscjlMfowa9fP5hKjBqEbHNZspyGtTLBafm5PPhhwfyWUK9mUs3n7BL4sC+cL+itBCKycRxYni8fOvCgZC/A3xxYjvLVooZEiK32UdHIkEEB8LI95F8m4vkB18wayWD6slFgm7Nw/7xouNmMTlFkYtiqiUUZQ2WQjBoIbHfh6psjhRVT3wvVOdXMXTuephguwdnLvx6vE8PMNiOFKrP6m3SicwUxxPmD434PVDuHUpWfcD6bhTSw4oemhv7XhTj6yCj9TwUrWdK3Nkj0Iem7/3umtIe5R7CO5Ld1ntl0nVF1P2Dk9ODvfbZwb7VxHcJlufQ+1qgWS1rD6aHQ+WX05bte7bInkcYxo9O44VkTpwemDE9g5CHquOR/BURSVXPxtpY+rrkA+zZ9a/dGy7LgQyAMz1VZMbTdy9S58PIUdLsyRRnadWWrbcgp4gP8s2UwBvxnko8jyTBRK17ud0fzh+4SuOjUl1Vrwugy+jCt9OgN7wYCz9j/oyyAL/5beVBKCCXRKDtraLjlBN25WQblo41yeYNTXoHR2i+PM9sv1Nlq3sUa0gimRc0NnX2pSQaNXvm1Krh95q6hsVdYGoBtmMDHZg6edXPdw5omi7Pbc2V59DZ0tp5trOQPPfi2cbz7Wcg7t+PMJfxh5omiSVSIIBeyXeWapUj1cvUdmEXEURoQXWdhLhinh35ZE6mspBFSWIsQpktJVea4K6v+9WTfjUXEMNeQuUdc6/HD/e/Vy/8RU6W2BqBCOGN/cIzc11v1yqW5gZbLB3WyAsO+cf1UoIOUeoFASTLlAnbQ4AcSAGqXk6J49y6i7kMvre8QRBG4lG/SPrFUfxhYJGULuh1RQi//gyu/OgNQYJiTwex7LFjNdK+GtpZjfwt/6C67qIeUNVzx4zMzk0FI1quAqNxfEq+7Hj4SJixW1f/5w11go1lFgU1jvXdQWv92RZV1nMjwJqnopapXf1wOj/mATYrLCpjEnVzqVC+VlATEq5Eh2ge2QGlnUKbauXTcvbQNakiLUF6bE+oEKwsZhdxwT46Y3QHFit2UH6zLE1RhVbat4HSVKs40kzOmZqaXRsn3T9ivMKMB1kCNT5/eScrMgfNuLGEkiXySbfVsmUyFLpClELNJkisCu+lhEjLaqsaukCkCvbOq63WRuvF2ou/sc7M7L6p0LUJ9KanhR+VuiEoj7KLWyQgM8upoJ20haKktCIjEcdn31jVl/y4sBXMrssnKpaAyYC5uaqjHCMCRSkI63z8AGR8RELjYb5PvVpoZnk+m8PGyHS7ginTHRejs7haSyNfy4Wy2jZB8HFXVNGQ8pG187VcGNm/E0lzRcpcGr5BaXcaFgwOP6ixG7JoyNO8ywGr64B6krBzmvPoyWovVVuuSd/TMSJOrfLI1FIVMOWO7RiTEsABjEMPC/icSuSnaykenom2rsOJhpauVPOd4vohoEoEsKQHcDaZiCjv4dPFati8kqVrao78Kaus6ISBR1Dty0pzVn2l5sqdcpWBKQVn9itKzdScRAqLNTwRhatZKhcvsRJFlkIxFYXYC7k+iv1rVP+qwf6VaFIu/5OxoPw1JrFRrx8UWwM3cCOb00fc3uZK5Ey9xfXq5NQATEbKbz9HKRw1VRWXqFEPxzqAD7ms5ynZriWpATVVIk+u3g8lUWEKRvUtFMmqWWBnXy+sgwV0gezhE1sQIeCE34ZBkxOgEiJwZBI5iTmdsZQNSArNleqB46eOLQtfIPKX/0apV1B2xdE1H6MGniQcsFzChmRcD/nZGb9oGKKz6JN4YM//Rf1rV3gX4yzPoqqHjAXgLN2Cu6rbb0lszOzdnOoexTGrG4KYYIO2xHZlzoVrfBkPx5fxrXss7u6XQDIgrkrd+/rVFasfc7nLBWoCiwKZuCI7jr1BwCJmvnA9EUK9hjxIL30vGsUisDMABekEugvLwOuDM2v1an2VDCyrfzEUb1dlXNbqXyo251bTMin+49r2EhUxqp72yfTs6ThmTQalJWry3u12QhDqiz4tWa+42nlQAai8N+GTpQEoH9DKaP3ZztbzF5sbwgX3nFZ9gHUaiti6eEzK8jFtQoPDqz7bt9TLedI4UsjqZFFpkQhakxnt0eo8ySqcZlLExtqadEthtvz5PiUqu7KYqw3n+eHs7IQmkyuTs+pOuAXCx+7bzyiR954cilOCC/UtVjrTsmVosNC+VcjDK8tyRdjwn/WVryCXxvIxOUum1ygkPxFZNM6pzlSWe5O+1lNnyPcrpRrNbUeVqbiPMNZZV77tX9sToPfwG7I5Cj8VwZmymAQFugIPT13ne6sPHIwqS7GugOoDKLj09oAvXVFo0S7zbePr47wL0AtZLwWLkFYyrazeSa3srRIFULPjaNVrKo7tyHoLkR3E3KTO8s6y5ggtZU4mayCM1bvkeNepdEUx2Qwm+R0Vl5QnPgW68smoSDVVy0F+5f/5P/6v//f//t9KC95dSW6Ci6tN+0V/q7fjbLjrBI5TdwSCyUO6kRGuCFDf3MwHdDPpUGbF/de4Qg/+yuDe9sPrQNmya1+HqDcl4f60BPsWtJc6iyOmFCZx9sMhdtfww9ElUHHv/dx4dGuEHbGwsbr+gKjSFzjyVAngkotcOGuoJixt8rvRTW93ddWSbyz441OlMHGB2VCtuoxiuIo87tC6Vngk8XUrjAarP23d7Oz98frP9fHLzWP72T8HP9Jg5+OFT3QBTwgX7Ugzh4iMlwdtfxxG+PqQPEfoMlgOT77Dv3JAy/lQFnNznLLmyl3JGb2QA2ZfOp3o4YMwWSmvjFxKoXzzFO/Cq1LVZuw/xWNScsXmKzMLr1q8Sl61+OksD0txqB8K1Zirxrqjz4ScfPp4y15wabamq803W0PvI4qRsaMaGE6YM1+w0m6omPq7kX1jbYMqevSSIsNVDvOuFwCyL4aNx5GHdj9fYWPL2mcHfCzJYw7XOWbAUz6UJb2HLatd4UG1B16P8TA3Q4EcEeBKko1wiNqFW9+ylKdU2X61HJzCYa1yZtvS6q87NUTVDkygTScxdLVJxKFEGLiBycdz8SYaZtR9SsdxNPOpPiCB/PQckIH+U+IFduw+22rCVkN1fjJQh7Dof4GeRy8tx6UGC/prK1DntfSdfVkMkUD8OpBkLwwC2ONh0A/rckeQseY3qhfo1B6PLcmFrtwozpPYGZ1kY+jXTTFG6qKOjxZ7cnPN7Vo6QFpQth59BrTKyyCe+TAIK0Pii25jxFl6FstVl/SFcVHAGprLyTnXUUB0BU2JxX7QyOI6IkEaoPXHEh0y0BbAEqKYT+tBKHB1qxrC8dSnAYXwJhpP2DXpWGEtF1NE0DKhIUlJ1OUq5UyyxGLx8MIcd5xW4QZdpfzmlzqgNAH6RD2czvqSDkJQHIWJe2Ev+hyb+1ncj9zDAd5bJmq98YX8WH/Eo70T5e7ODVjU6/gYtFMQoNMAUdxXfk2II3KmGukEQqzJUYP0QKsw8v4kEetCjl/jllFC5lxvBQeuSXXpLj4e9cqNA/9EIfn/lllfrnduXIzww9JUCw8pO+ZGw2KNveVAyFvOBiiMO/CwqvxSIzsTwCp8a83XVh9Lm6QYNlmND6UDnXoyVaCtBtC07RExhHFrkfqAqBG0zsEN7k0N2US7uqQoxRfuVSE6Z0pX5XK0uMd0ePI1FBvihekzldJT2PT+WXFExRb0u0+dQCsBmpGIWsdZKUr2AM4LJBHtc5NXevSFVa6Y0QEVFfHARdmpPOyAuQRw2LYe2l5vTVmXucsqlrlE93Ea9dxcqUu1Qj0UOFuksoAQANXkB5lZhCJcVpbwL6WjEWpuwINpLJntyMKt0fMXFV9R87S4fQ4s4nFN8UJQAIUWvsMRHqVYCooBUmFOFJDxREVkPNFPV8ZSEOCongiuY1+2rb+JrMv8fZxkG1CrYi8EL5VX9WTXkuExIgAozkfUNCwRW6J+zkWj5HVjNRTn9MD4MjkPVY8SxhkNNmppBfDw9UyGhFgYBCHOnz4DyNlcK3EEpRtZkVY0qxKA8k1Q7wtYk5JlY6cJaQ3xtAblp27kORgqLWI4GqIsNAnIyaTBOb54ZIQ4Cy2448Qe4ONDeU0oL03IgnYYHKKrGPbVztYfuwA6en72618rXdeO3KhN8siv5GdW8QAIio21NfynaFYj/YsiC7BGtZNLn4NSk2s7gvj9s8l7b4qo+ua7TPGpwkjRdXVqPzxATXOHS++LcmSrv8eL4PzrDCgqeREOvsVbruqqQLP6EiuvExRWqMv6/C7ngeTfrkOmLu65Ob/nqzDqeg4ggtZta363t2HyCrbncIed+R1AO+4DMHlLGxt1tgQ3Dw8epeADwlPuW2OyszA8soOJgCIpQdt1IH8IRx8Fts8qlILI9lqN9cKpvbYT99qeiLPWCcDqX4oX3VYSA0CY3EUvkQJsgO+P9EYLEINF7/L8q6zLIb/q96Lee7aZHPzblgMeJR00ZPARk0EWP2aIQcqSasmmlaJQqVnLaos/6cUgUcemFqiK1JFcfUA00SAsvuI8qPJDTsDCqD1JVJXQlYroQXonJ2kP4zgGxsuYVzWGRmxfiv2UaK0PG3qpNmvkJqIXbzKgGIJhCMbqX/zHLKmJW0yVl6Tmk+lC6tl8lU4lUiJ7Sa07DAt6Kae/L3Ep7x4ESQVBCw3+51/bzV/s5p9rzZ2L5oe//WvZeM90sBi/lBde5otWuWFkJhdxCnljzyOiS6/lQRuqZKiSRpVWi4W3Z5AoK9e2il7ZzpVNnmolwWg9KGbJFmO1rLOhF+ccUOiACxwRrytzeJHg0009H5/DxUk40iUgEpZUxgWsm1lPclFUby+/IUMBv2EKeIJmUBwSzqhQPd6QQkMKgRT64cALmkRGpit61IhpzRRrt6BPaJHOVDTsINJTFQkhOjRsaiBzBTFJo2fbgvLyrLAZROAFtbM32PeVWLAhcd8wiUP8y07bSHqGvJXI2wzKppOaKbQt10QzXTF5IgMWv8Ju4kfhnMuRO1WMh3JUeQGpuBjYjI+083asJaicoXCPgMLp521onKFxZRq3+hf9O9/oxoRrjumNGy2mWL4R4z4+YtSov43KKZiVFNc7x2NZd/364GIXAlMeLUXV8dWQUUNGK8noKr/6xQnGGMieI6f8cHw2RRVtCvps9mLaca+8HgqCDdaARa7nLL2K9IJwmlPfFz3qSYm8ekOZvxXKTDfmZejQI/baVHDO45ReODsZrEjQ12TEW6H8weJDRbG790OXsk4BbudNOFyTgt51TXtITyv5cCtqtD4ONnRavKJGsjcsqWycFRbQuJoTcXGGSptp+ZGZyKGdvY2s6kbpNqVBV6X2V+FwuSgcTEUiStvkNQfreOQJwzEmqgtUXA4G/xXH1+oLz+VrnMSerktHbfPxMbZPxhPYJr8ou8wKE6DNvyLhrF/wI2iJsvWcwKo2aRjKSqIiJT11oyBTySMXXOJBFZ5ajI4KoQXeTsfQ28bsOhC1VpC7Twuv4PZxccncVTes0bDGCtYotDaBJXVtYFP5JDbU81PjyxrK8z6PddY1mD169rW0XnY2W3Qp5QHb2Jy/7Fpj6gqawrNHa0EzRPnxEmW8Poi5BT0Eg+D8evRVtL0XatqjsQxBNQT1qyaoeyU0Nsagx0hcl5Z4V+PEHcPX+I8QgMvGok7aVWYZC1uWTUTcwqMUsDLhYiYHu2MWg5cm1zGNz3jO8xta/U3Q6qU3tSeQq3J6wjhcw9x91BtGbYWvyWeyqclqYNPcLJjX4YKu3Aqyk/BSvDXgSpEXduBcYJkNyrVD5RoWKkmVn/W2nIcUBhT1gbJSU5ycFn/BtHxZnSKNEnDpc3rBPWtz9tiDT9du98pzr+GvbhRe437dm4Qrt5UdST+E1/xyE48NEzAr3i6sYynlueYV8aP5wHUdSgzisI0t9eIhWadHXRfzqltiAWi+Fiuw1ArYrGjHSb38tDZFBfIYmHcN0yljOSMnjTiAWRwhrb9BpZC0nIMFzxfN9cgcX0Z7fPQCjiotFLsyx2PJXtehn2T+33J4buF3y+5FIVAFKmKkCyOy3FqFNEIjHGUTfAJhpJQY9o0HA2CK/zCC60sraFmvPMyUrLYiIoXFy1eswgWN8bkY0LQJ0DAHiymeq+8pUzCSI7uXpDZQAsri6jREFimucYyDYhr/uGWJdBm79Eb+N2A0QeBGv61YAJoh1WkCyim/xqq9QfzbSt5Rw/6dWJUK63s+7AozY2MqJsFAQhotA1pePOHh85y9Mr/U4waYEFg4W1Uhs1f9DC73mGtMEzCfb2xvvRBVsP69t7GT/HLuJ70N56ef3zuYM3PpGncEggKv/KI51GZBWkk6mL0MJRxKTVYWax54njQtvok3MB/6D3xHczK/VR1qqcDnHOn5jYfyKBMlKoYKyOb1MY8np17M07xf51U/aaDStr72fJNk/bl0scbcubyP8d0hsGxqqJpLz7ziRciNbFjADatFPSDiA6xKvliSp7vAtLCwYpKqOpAtEYxBFKZkAoGvq9LqvyIegcyBThSHgatEvZ6QcYb+ZIEH7hAPw7+s765LQWj+pu9nnhyuUcL5e0A2RVsDbCaL0ssi9b4XXK6I2vQfKjRb2paSEWB/XJkFJ4mZI/z6BEZ+8oHfawV6A6puycc/AVXR7jFgYsv3LrEfreUJgIMW8+SDUE1xoCAU4zCT5+pbdU/hYSxXHSb9gqe5EIZPwy8uwwfINQL8+U5tNSbLDctAqK/hIzqqokhC2lNYNUlGcVUf/uUp7d/O7itJd0CUeElUyjZqec7Txe7DQ1hvZrPi6oILHIOqToL1CpuY5r4sGCID5Tw7boZ7xL5VUURaVC/BGt7IppnQH3aOrRfP1tYtHJsy6H/ntgYt2PvG2sZWc+15c239bG1tl/77yxPcbtUvf1vbgP998jQvgcgajPoQNQ7uIW1GCh8wYFsIhl/u3FRF4Fp73Vw/29jc3d6B/xb2qv0y++D0Ie794D7xbrSTe8mS8PyjyyopgOrvjZBprSFcYEexd+UeyS/ZdD2yb/jjBlkHSLiVH3JJz7mdxcWW0PWiYEMxg6hS5uC/sTYf1nUHlUZ/lIhqQCCrvVAuOy42WGW3P7gpHPrIji6BkL4Jr62TyAvRGiNJLdFEqQzsikKKHZeiJWmr+IfEItDq69D6Tzu/gqLL08CoctBPB9LD4j3yApbR5N7s2DpKtYdGuC98hOSBzIsln9OYbSJkZPfQP5TYGKk7a+ei/B6xsWsb0xjZ/MA9cvseBgILw02NI3nY61dHKg6aVlI4zEcUuNDJmVBN0MIjtunTHa1h0Kd206z5fNHxYnuJrye/D8kwCnK2N7bR4QeXMGfl36NRp5j498SUnyY9tjGuGuOqMa4ucajjyCPSgIa8a8BcJRlRNbermSYxL+iGN8JW9Zsc6bcV67sALoHoTw6doKkPDor3b7nZflsh2vKbnFM4bhC9hXKOw8RDfLSEj7CXsKp94aVqEgus445yZ55kkwCFP+G/hC6gP0See+UKQk65LIc2fE6DCISSzJs7H4B3Gl5tmn9ABXM5NW49p67p+tp2SV/riIcjo5KKJZYbqg20rHe27zlWZAcDQIv1Jo6tHrvBXdteqwui+5twYaWvdKOFKbuhbNtouP2gAU1YcgvbEHZLKtwJI1duQDpfeQq4Pmi7ZPKjjh1vDM0sfs4ZrvnqBHjzYNgB5m6FS0JW0jqg/uJrVKeT1DOu1wq/k851svxL53qFo1z4x0FFGaL+lfnAW9Z75BUjKr7sy36ohklu0aA9jFLQdUZ2guPv2bHbBOEBw68SIGR1yOkDWejyDnl5RUiwxdPUxNk4f0dUk0oRmZIlANvnVk94rWi9wc/W36TI/L31RJ8ha1chRtehNJ9q7sy+TUkgPqGZlK8rSU8Y7YfBH94V2XdmGRTrQOdepsmoL/Rqi061LZD3D5VZ9vH7A8rsWSphUtee/hld2TKsp+SG11M7fiFvdq21LePQvqN98uHY9OrbJB/Omo0dssIOSXYeY4R8xEbIquJ9qxQd2ptS0lMaHmWbaYZJ8TvoobrfXsYFpSjyg4wfDbIaVKqPMmOK28u1OzlKIyt4b/lheJmOc0ZNuahpds1s0aby36eqALz8c60bIuxxAYG6k5Z1hPIwZaNxh/aVh1oi0EcMacYETMAABMLUAO79TDNTr3l8fEQA0bASw0qqWckqZrSq5CeyIkY1N6FfBZtQV7WfjwWT9Ie0JTa/N7s2Wv4zV0w9LoGLNDziC/MI4yw0zkLjLFzUWXhHV4yMclM0VsW2WWN6YzWoZcOqNczybpNaORWFYEfjcaE4XozSF+pczrpjGTkwq7phpEAjBc6PZypx+FOmDyOpewnXnu76kO4QL8i7/9AswKkXIrhBSF20+FxlyBUlhHoYYsrclPKkOuINAtt2A+UMl5deplxlebNalGyPx9NsDfcoP84ie7CfqElOPPUMC8QR2ic+ZtGeAksA1xAulxnUUEKmhOcBBZYwshtS+IhJYT/spVPSkL/Cn2T6ZoGKFuZNKkVFVTYEYhUKEQWkSKA+ORsrErSG9Xs6GqP4ZUu6xjGhkdvsexgjgWYwSjQeWPTGbS5Zo+20xzWSUC2bFmjWtXolZj8Msgv1WEiK3LuhJo+YmnAY+HQLWmWUOP2kYhutWBXMyQcVfDcCCoVJ1fHRJQlTT1vWXjjqeoHLsZHSuIcluEOq4YgRW1IqgjHRdFYjvpwsbCa63BiMjMHoYafuuM9on2mJIXIZfyRAVPmuBxAKdPeFz4sTekSK0RtJ+o0Y8xjFmMb0akw2iSclkqN+FRHhueBvvm66kYgu5qnIG0a5Lqk/G3hqiCbcfI9X8gU0nD01/2PUcbLdG/LwGMnDbe5RLFZeq7acyKJsgiZUEo5TkE18qiWKcaEyBkGYTchWovVGmpGiXRN/lmkH9UJtWlspibkof/XxOFtWWyarRTuzMNRcbbU2Wi/WXvytzotb3NCXozodOb0hOoboPGqi8xcn/asuhnWqP6S0HBeUfb9sbqls1RCPOjjgNbEdO7Fz7i3NkeXD2mL19q0G9YCF1qMdS1pT9jg3Img8VOIAxK+ELEJxiGqOO+b3lKR+yTyKWs1mLwDq5vusNxPt1FJ763r4nDyPcx1WYpkoIubWUkrpmIt/mhNIUXyloQIrmnpkxXYusmJ9rUZWIv3stSRCZLhprnN4m+9/z7nPrj34dq0J0+StA+v4Bdv3AqfssxRRFyn5ADbW8nNy0UiWitUhwID1Azwe6GZkFj775iRrsoeq9+PVu4k+GMb2qBgbVXThMIyMh52P8aFgtcgsfovT8Zh9BKxjU/WMlvU2DJruaJxMLCey+wk/A2b6L1NyE3XXa15QS5STqSM+24KbTV/+GxJWKtnqw0XnaAxqLB58xm5CgjfWceUfyFmJhCQBQX44coOkTi4bAoHhjktzxztoHLMLvNCh1iFb+9hQKCYNfuZddSiz027spXECIOdcvTgE4CKjefF1R67QMDIi+gIWPvK4jFTZwr3oYl5nBizth1Ug/F7vjuvyRoMpBVlmL+kNoQQmBY4nceKiUS0ZcuSAZm4D1ARNUYQK3HGhcVskcdEWmz2OLfgduCnibhqIRC5qLjGcniZw/ohHdnTJw+G7VG3dsYXVuGX2GL6RGUGU+Wu10tZefILJEups4wSD0HDOsaeBqs4U/FC1xrbSREBphH/lQCT0iYObsRdNOlg7O1cTqYbIW5Jq9z30tAAhBRRrysg5fIQeWVRJiuagkJM8tc9v+fHV5zbS2OOTxirNDKsyaVelmbOS7E3xk8xoy6/lZc6sf5O5oUYhtGD/pujM+bVlO5WdVTbs2r1LSi41dDk9Vw3xS4xmBLCHJ4DZs/nvjGQUZwKDbIk2ClUU6k0j6VsPjqQbGvzYaXDkgoQjMWyOf9qSjStSHqBHSv4snqeyTJnogbkyR8/cHKxk0z3NpjPE8wERT4UGNVu2k6qjqJF26kyl8lfGFYVj8TBMfceC0QYguc8qKrC5tru+0dre4IoQjhejLnYcHAYAfFiKSEVdQ8EQZejVEry+FYcjF72heupxqXiUSncqaJTCBSWm4+u6vjdI+S5MGQAAX13/0/AXw1++pMXVcRHF8gxkn76by0D2SDXWgw4yZkJRhbVjmHAgwzzuh3kY6mKoywOhLtOkV9upFlzJxmkruyY1rLSD5ts09GdlyRAu9WCoC7DSzsf3Zw4tQusqkyKa3JChByTDioOE+eZCWOVCyA6fLEOIPZRDXOBJ4cHM5tbmzs6mMe8a4vxIiTMn0q9FnkXTOQSaW+VItBAO70afz+XshkJ/exRa1HlAgBoCbQi0IdAZgQ7CxOtPmrA0bFZJp99SE0s0KZt9MX2CReMIZMSgjrEn3O8RUOWxh7FQlDrBAkoGvyhbnspxy85woHVJy9rj6CnKBJ9gAVuHzMmeMNNhoMXI7h13voebnQUOiKFUoCRaEhBeNbgAQ6Etd2h4wMPhAbf5Z+UUR9cUTWn9hnIbyv0YKbeqWrU7LRuGVtdqSkIM3WUhZOyZ+WOtThajJf0yOnmVJT2rU14cZesxFPZ+3mmY9B8m/YdJ/1Er/UfZm/HNCglvNGJr3nQ+RpGhKs+E0NQEz5+myNErExXKHBZT6qmod1Szxv4E3bTQSrlspUSRJaGwLYyO134rGOamywwx9DtSqzUiw8NRylYQS2gM3z/uE8Bn0SNxiFh0RL5jKRJeqdpzitUT38YgGpmdEW3ATngd5F9c0dOKMICbgp8ACUXegcgTqVrJaKCsAlT2VVgLLA48SjgRLGdCAmQ+C4/qGxxPmPVkN0pYHBFrGTvsmALixuhGDHOBDfIOwsTZa66F4dlWXadBtcMJ6rI51AKJY1MVWLmUD4/NCNrJyIthko+RSc7Wq1f/Ut6HOdkQpvPUchNUnfte3k/RkBxSUR96iHBEOd4s6B4wi+IVWiMbq4LCkILe9TLeYJe5w3w2CxszXPYzKeZHORGowhE1b4n5AeRLf8W15sRvfbO0XCKwoeCPioIDQUoLVPnA8ZIZFJl+pnLsKCAK3MMMHhXiWcuSRJPFJe3hvdWzA7Q3AR12YUQ2QMwntdjW0NpvltZ+Sk1rkXIYb93rPH6jIpQT9m/zjwpokuoHBd8syzjI7qIR/x8n85j5WGEaByk2qJLoW9ZJyebF+WKQaSgLRtft2WmsvRzKuAovzcHC4L5LKruoLzOfyXBXw2a+RTZTta7sqVo5xUmNR8yCbTHG4XEgswClM5rgo7ZZgTrfZ++eGefYPyOZDZI7uICiwBQXpy/mvZjrc3oIq1SHAn0OxJRznUvmqYzhbV+DaQvfz5C7dMrr77bjkBXblj7VQtqN3M9T7Nz1+JbtAN6paQzX+na41idTjiTe/cOdVG15lo4kUc26dNk9A4j8nTsKf/ca+K48Snqhwwlhepw6jX7D1k8ROElkBzGPsFB0utaPcAvGd1wnVSAQqT/RhRTZvUscq/wCPNv0I9PZ2hmJMDqb4Wv1+NrqX9qNueWrUdb7Tl1K8zSD1YkWHNQurjBQDS1lXVmU5LdJy3LEiGY0TPGbZIpL1/guci7GkkWY1+xN3HF8tW+dTz1Wv9Rp7gYbnvWIeZYdxy60wjgmP5z2JnZf/Ap8CHPRlo2P+d8Vfxnd9HZXVxVpFB/PT9/IfIWOe+X1gHGlQUAhe/CVCB/Y8+l1VvvkUCv+olF8mgdG0lnWK8yTW2F5FKtr41bns6tPUcllX1/CY6zmkgOAITiG4MQ4Hg5RCmCimaZRmk4C/Hsk6czAu3KDjNA0dDJDcgC2EiSnZUkcjLmMLiZJQ7dGzwYcdbQI4lM7GLjStBuTMhy7Liq9+CDJtyP4lVJy16A9tM2ahKe2nDxLDHsl6CKHVMLkLatN0nJcB0hxa74gducZlCiWRv4yryFoATEhwpcjf2EvcZOmWEWVbaicTbDrBYjhfBU21p6Vd0aFUSRz+9p3uFnlbKCzw1s3Ch2v77mO8S98NhazXoFxYssgVURE9fBoYjj/uO+Ry+5znM5jY33peLqkfT6eJWfnfuVnMIkLs0fQmYiuePtGJp3UTzwsxrSK17OJlc201y9YsAnHopLRNjLBAP37sCc3coMeufjR3SjiBbKwAlSuMfMuf8slYLIItBockee9syxesb360uh5toYjOc4jE8g1EBhx3IjjfCtX8Wn9s60apMn6jps+nUOjmBbZ1o+d47dWF64xx6xy5yZQmhAt1QKjPwN9sto+ATChUjVshFCkRMypitstRs1eMuy+iH1Bu82P0bpgiJkhZkTMxuNVwIF0PL9KtTAzQg+LelRYGrCZrHeXjmUPgcRIaMQ3vPyWdQa0pI9lqlSYl/Kvydm4ehTXs8YSerIeF5OsMNApToeWVY5HGY/pF1pfntrU0p473iBoeoEKXaOpP4MetsBVnx1dQcclcK1OkANBS22XX8NieKsnY/Nu4JgFUEATGpMJBL84DI5cuOW5clx2FFEuLg/k7rg8920pEElVIJdTjHhMEennxYx+cn2tUlxFYbvFpcGZ247jcRzHiQYoCi80ZPBxk0G4UJ4/hQy6FNKDuCdRE4WxaQkY9DZScKJYApoCIw5gUYzViqYuQM0OaKFlavYpwrPuTEDYGZVFWMTMKiTtUECfaidzxW4Lj8BykMze7LtF4NcgEjxDHeIwP0C4o88eu1Sv2djsDM36RDRLNqwmW3tCUSmQLs4PV0W/XnmBFw+rmlM1HInT02layzrsZz8FrutgAg1NemuI4CteNsgVPT91MFSpINzBZOMJiX44EvpAQFIcDADuGA+2ALE8lRD6NuilfuBzRtYpkYj10o+sBmFUky1BG2dI00p/fkhyNFxskbZlTj5rSukwHxEQ0LUQAIDiOggWoffQCWH0GcnHGPJRZ5g9Wa2cg0REf5vME2fhpRvMHyKvd1ncF646dJbGJiqSDsP3yU8ZxUkTTUETXYqBWYfhyCVnZ1R70qwLeUuVvJ+VEC7gqgCvBqL8bnOrmI2+JTUkI0UOxoJCmzkbBqSXiDqHOmDm5CqgyIyERDGbfjjwOPedh9N7f4ry1/RjLwrjuInEGSsWS32cf4PNqhTO6ksge0BUYvWZ9xFPgh5dcRvo34dp6CR5ABJiN+hFE3r0yPZD1NUFp6CE0P1My5+q9yO0vqob9KVwOY+90zYs76rjJkDRgfsej7yEIpcBb311ejC5pLksdG+47nz4K+yrVbb7/ZATdU7HkqEdMzcTzwYZLztJGNWr2ydnIDbuUmhrzJ1xg1rEMpJ9vCQdviMLDK2fE5nR6UIBI45S8vvyAXHgB9+1hWBDwwv8gr/kqPQOsgA2MYm8u3MPa2TDUFH+Ec385aga7ZZHpA4oG4K1BM7Y9fsCmotPITZMu60eHi/A0sPTbZ8x+MgdhP7opd27TMeLj146FyohDdhcOVfk9kJ8XbrYPPhggftVjVogFtlBl46lBMjy5vNLLFGWg4rLC0vSFoSJ+zU0pl1DC57hDmRCwbUEU2JXHeBW+/hmd7HT49i18lzI/EpUEegTbsN1zkEIXuYcFTZS+c9ohCkN0R8H3wOpsq/os+tFueMu4s1rN0A1xs2XWwXxoFtRb/E88G6yNN5ESLQSqxpOXQPtFcbrMkrphL5IlgukVCN7GXHSMaB4XlPBWgv3MvEi1YmuEHDmczBnPtN+p40oDN5jO6Z06vpklnhQgc8feCkqD/Z0aU7KcXxXylt+P5zwZcrNRI4QniNLhDxjFk9UwIWvwjQZhPhnxVRlFiSYMUppMI3rCQTNrYbJ/zyxNZKRUUqihE52fCH//iO6iDExBjVwGC96VIbXp6+oyMd0KVQwyqZYdw5atDZB6Bd0QGTr/yPiBa98KDklcvgh/RFZxRIl3WmvQln1kkSsnT3NXnRZ/HRLwYoA2RLEpaPgCetuxdLBBX+LBvP2Ip7WMYnCvCZXQAIclVShJzUABd8QEei8plB+TNimC+SoOHk9b8zhBoXrlU2wz3flXu42LyIv0HM47UI9HS8e+/bEwq4NjIS+DMLroIKOOmUUzg00HwR/1KjGLcO3Zu/gp1O2/IztCcVpYO4MsQ884sLMhZ3QFKW9wJC5q0dhckQf7BoSKr0vq0T3omUf36HFSFHQ1ADrRi6NCApTdgFHclK44/a8EWjo88d9Syy05sAFeIi1l3kWrRVTFPPgPKAXC+BUQVoSu7nwIgPtYuyLRqZjF1KFHVezjoIUAdez2bd7iJRX5fHUe9ciVIQJWUxTAk7F0oSeSpeoQMXD8bhSNkEKJ1ltI1cZgLlSRvcraO5ihp+znJaXXyCczYAM3GWrqVgGKdWLTSj5G1mTFrC0UO0F2aNKRSADS2aXmrcqUPzokWxb2Myl30CzneuFQa4Axe0gecsPEzocA0WvYeelehiAZMM2+lO13grxWvA/fFBRwqrDwKHX2bGSrXVfA15qEnOz3LrK7JLfBHDJWJafatxbnARJ3yqmTlOw6ULC9pyaxthTDVZsg815BnhvSnlGztTJ3CN3iMEQo7iZ3yVWIoHmxmEjSziezKdjArmmSzl7wxAutzUJ00hNWqYmZziMWkrmFCLHyKyjlU1PfLvnDqmMyazVnE9dgTZAeR2gEvnOHBxLuyOvMhGkgkQYAN6kFXN3qK/VTZMkxGdVXdefPRkQiNmy5csJRZLCdPB9A6Fv2UDhVLzCGQ6AWVWxDA8SJdDhQOwBeX3ACRROIu/K7k2sk9D3eqDGlk+MRsCuY9EUa/Qx542H4bXFdWEq47qmbaxA6Ri3qs9YAVwCY0HajP5HTWhy0ojCZEuLLed2qaByJQqjEwNxkxal0qcVE9Wi2FUdRfEAE0vwmGMJEC2YOlbEEgi2KvCfH3pWxYLuVfNfIbRkNjKi+hgdoGgoUxl+HVkkQQt5/MU+vg2P/ycVGqrGnPVi9lzxXdcHYGSLxgGVy3mcnBXYTz3Zbo8tF0XRjvgSabAoIvyaSLSAHz98N0yScby7unp9fd3KUtGsUqNmGHRDO8JXD08Ju9AFRXWkaSDJlsbEwaYOJZo9XYAvzST2CvZ5YN1PjEVb508PK8LCxFWYuAoTV2HiKkxchYmrMHEVJq7CxFWYuAoTV2HiKkxchYmrMHEVJq7CxFWYuAoTV2HiKkxcRT6uwrjlHrNbDvuX0r6+dstJWSx5YYo+IZUMWooec4yJi1wZTOQCQppMAoW63zjtwnFJ575KFhLiSqk8HvJL0ldYRNKMaWhHdZ3v8/ewwXqDzY4bNpsv4gzk8t15Z2Ap1VTjr3rpVHOUJQf1r8TNYizlxlJuLOXGUm4s5cZSbizlxlJuLOXGUm4s5cZSbizlxlJuLOXGUm4s5d+0pdwYkx+1MXlVx6hVKZ83L1nJKD/6eMfGB8oAqUvzFWqAH/YuK2wIWfZIYsF5vaO2FVe/yLoI/8nqB8x7yFqlnNWs/KrpRKyR5V9QFO52QVm+jyyMmsE0HTtf1ysBExFu7NzGzm3s3MbObezcxs5t7NzGzm3s3MbObezcxs5t7NzGzm3s3MbObezc9xgRXrGGWqHfxsxuzOyVZnYcwp1Sa1MkTsIsezNt7SrBUrFlppGwbgRiS5hQWkLSr2V9TdjM3U3vp+4da5LfxQost1KtPE7jdnL/OsiIPiqIod3ME2bDx25pn+PjKHD8Aia2rA4m+PMSTmtDib3iS82IFJKWzsA2eV6MVd9Y9Y1V31j1jVXfWPWNVd9Y9Y1V31j1jVXfWPWNVd9Y9Y1V31j1jVX/U+R5yZVpKdpSZ0fi1k0BY/wAxg8www+wKkWNan8A/1jDISAaqqz5JcdAPAxT37G6pF85FSa1e3EHiHV8Ka/AHaLyi/brUk0GOi5Zk2EGYahDDr5xx4Ex5htjvjHmG2O+MeYbY74x5htjvjHmG2O+MeYbY74x5htjvjHmG2O+MeYbY74J0Tem+QdkmicSU0qy/gYkTxIMrvLoH5doBrUcuwEWmhVVjKlXxd2JW1RQnaWkUOolwmiIuXJAagUd+L3b7YSgJySoPgWukKSWstLHuLo6Nvn5pWUrQfGVmKWL8uU0udNoT0Z7MtqT0Z6M9mS0J6M9Ge3p02pPxSUSGTeKi1FclOLSqIgM6iCLrsKtcjgAtWTHLYe6ZTJbPwpHoK2E7EdWbnTpy1pO2eDHyXXUjU8RArSkIN23Ux/mlt/Vk63hc9clY4Tmxq8bppEPzkAGwmfUsvZ5NaQdovCUHUyWIPSxhxgZx5dR3YzqZlQ3o7oZ1c2obkZ1+xKqm8laYgKdTaCzCXQ2gc4m0NkEOptAZxPobAKdjb3H2HuMvcfYe4y9x9h7jL3n27L3FFZoAp1NvMCDjBe4nRnovPpHtIoyGTnLy3EFyvghL4DgJ2XrW9odeZxsRLIcIep9mrCCn05xbStfKKxgGu+dlVKkyIlz4EG4hZqMMJ0XG1e/Uf2M6mdUP6P6GdXPqH5G9TOqn3H1G1e/cfUbV79x9RtXv3H1G1e/cfUbV7+x9xh7j7H3GHuPsfcYe4+x9xh7j3H1G1f/o3f1/6V/PNy/xeFLSc5eu8nsDALYoCQuWGEflNqqa5STkpbz9b+uU1gcqDAw64Q82L9WuJNr+egLqoGgqfLWgVBautClHlLeWMkDm2y78C1qEyv10qu9Kwm9K8ZLb7Q2o7UZrc1obUZrM1qb0dqM1ma89MZLb7z0S7jfjJfeeOmNl9546eeQCeOlN15646U39h5j7zH2HmPvMfYeY+8x9p4Hau8xXnrjpf8GvfSrQtrYrXqg36bfamX+F03twJKCaeWFEiLjsr76thSNjLveuOuN+mbUN6O+GfXNqG9GfTPqm1HfjLveuOuNu96464273rjrjbveuOuNu9646429x9h7jL3H2HuMvcfYe4y95xHZe4y73rjrv0V3vRAyKt31e/RbLXe9aGqzhHHlfgpn/Z6Uh745Z/2nSPpfj5Mcj5lsWEIw59UvzEzUMKnGVXJD8khMxO6hKIAJXzDqrFFnjTpr1Fmjzhp11qizRp1dTJ014QsmfMGEL5jwBRO+YMIXTPiCCV8w4QsmfMHYe4y9x9h7jL3H2HuMvcfYe74te48JXzDhC99i+MIf0aoQHZpSWKsOZeBGluQw2LhsTRONWLoI9Ar3lhg9J4uKwZaNZ/jpdK8kFn1rkQ3Gj2/0OqPXGb3O6HVGrzN6ndHrjF5n/PjGj1/WPIwf3/jxjR/f+PGNH9/48Y0f3/jxjb3H2HuMvcfYe4y9x9h7jL3H2HuMH9/48b9lPz5Q8VWWIyrd9x38ySJKODsPgd5QI4j9MKq6Wct67jvtTkcKPcZjv2I89kaDMxqc0eCMBmc0OKPBGQ3OaHDGY2889sZjv4QrznjsjcfeeOyNx34OmTAee+OxNx57Y+8x9h5j7zH2HmPvMfYeY+95oPYe47E3Hvtv1WMvZYdKn718TV/Da597eE96dIFSKjI8spPe0I3LNHhpR76Ye8W48o0r36h2RrUzqp1R7YxqZ1Q7o9oZ1c648o0r37jyjSvfuPKNK9+48o0r37jyjSvf2HuMvcfYe4y9x9h7jL3H2Hselb3HuPKNK/8rcuV7QT/ErgO34Jk/dYEcuFcgB7KxihoWURQapRFICYCiDQu4RwIkbdQQXRqAqc4YBM4EeQvL9g3rGN3lDULk9263E4Lsn6DkZCuSSAKTkA32fA+vQPvkUAyad+JLC1jeiw97OeTVat76Ws7uabPK3T8Yv3fVDPwrIXIAZBBBcJwm4zQRx708gtF//n9BLt8J870EAA=="
+ SPEC_TMPFILE="$(mktemp)"
+ trap 'rm -f "$SPEC_TMPFILE"' EXIT
+ echo "$EMBEDDED_SPEC" | base64 --decode | gunzip > "$SPEC_TMPFILE"
+ URL="$SPEC_TMPFILE"
fi
echo "==> Starting mock server with URL ${URL}"
diff --git a/scripts/utils/upload-artifact.sh b/scripts/utils/upload-artifact.sh
index 439b398..54eeff9 100755
--- a/scripts/utils/upload-artifact.sh
+++ b/scripts/utils/upload-artifact.sh
@@ -20,7 +20,7 @@ UPLOAD_RESPONSE=$(curl -v -X PUT \
if echo "$UPLOAD_RESPONSE" | grep -q "HTTP/[0-9.]* 200"; then
echo -e "\033[32mUploaded build to Stainless storage.\033[0m"
- echo -e "\033[32mInstallation: pip install 'https://pkg.stainless.com/s/beeper-desktop-api-python/$SHA/$FILENAME'\033[0m"
+ echo -e "\033[32mInstallation: pip install 'https://pkg.stainless.com/s/desktop-api-python/$SHA/$FILENAME'\033[0m"
else
echo -e "\033[31mFailed to upload artifact.\033[0m"
exit 1
diff --git a/src/beeper_desktop_api/_client.py b/src/beeper_desktop_api/_client.py
index 6c43fcb..6c7b847 100644
--- a/src/beeper_desktop_api/_client.py
+++ b/src/beeper_desktop_api/_client.py
@@ -52,11 +52,13 @@
from .types.search_response import SearchResponse
if TYPE_CHECKING:
- from .resources import info, chats, assets, accounts, messages
+ from .resources import app, info, chats, assets, bridges, accounts, messages
from .resources.info import InfoResource, AsyncInfoResource
from .resources.assets import AssetsResource, AsyncAssetsResource
+ from .resources.app.app import AppResource, AsyncAppResource
from .resources.messages import MessagesResource, AsyncMessagesResource
from .resources.chats.chats import ChatsResource, AsyncChatsResource
+ from .resources.bridges.bridges import BridgesResource, AsyncBridgesResource
from .resources.accounts.accounts import AccountsResource, AsyncAccountsResource
__all__ = [
@@ -142,6 +144,13 @@ def accounts(self) -> AccountsResource:
return AccountsResource(self)
+ @cached_property
+ def bridges(self) -> BridgesResource:
+ """Manage bridge-backed account types, connections, and login sessions"""
+ from .resources.bridges import BridgesResource
+
+ return BridgesResource(self)
+
@cached_property
def chats(self) -> ChatsResource:
"""Manage chats"""
@@ -173,6 +182,13 @@ def info(self) -> InfoResource:
return InfoResource(self)
+ @cached_property
+ def app(self) -> AppResource:
+ """Manage Beeper app login and encrypted messaging setup"""
+ from .resources.app import AppResource
+
+ return AppResource(self)
+
@cached_property
def with_raw_response(self) -> BeeperDesktopWithRawResponse:
return BeeperDesktopWithRawResponse(self)
@@ -188,9 +204,11 @@ def qs(self) -> Querystring:
@override
def _auth_headers(self, security: SecurityOptions) -> dict[str, str]:
- return {
- **(self._bearer_auth if security.get("bearer_auth", False) else {}),
- }
+ headers: dict[str, str] = {}
+ if security.get("bearer_auth", False):
+ for key, value in self._bearer_auth.items():
+ headers.setdefault(key, value)
+ return headers
@property
def _bearer_auth(self) -> dict[str, str]:
@@ -272,14 +290,14 @@ def focus(
timeout: float | httpx.Timeout | None | NotGiven = not_given,
) -> FocusResponse:
"""
- Focus Beeper Desktop and optionally navigate to a specific chat, message, or
- pre-fill plain text and an image path.
+ Focus Beeper Desktop and optionally open a specific chat, jump to a message, or
+ pre-fill text and an image.
Args:
chat_id: Optional Beeper chat ID (or local chat ID) to focus after opening the app. If
omitted, only opens/focuses the app.
- draft_attachment_path: Optional image path to populate in the message input field.
+ draft_attachment_path: Optional local image path to populate in the message input field.
draft_text: Optional plain text to populate in the message input field.
@@ -322,12 +340,12 @@ def search(
timeout: float | httpx.Timeout | None | NotGiven = not_given,
) -> SearchResponse:
"""
- Returns matching chats, participant name matches in groups, and the first page
- of messages in one call. Paginate messages via search-messages. Paginate chats
- via search-chats.
+ Return matching chats, participant matches in group chats, and the first page of
+ message results in one call. Use the dedicated chat and message search endpoints
+ for pagination.
Args:
- query: User-typed search text. Literal word matching (non-semantic).
+ query: User-typed search text. Uses literal word matching.
extra_headers: Send extra headers
@@ -454,6 +472,13 @@ def accounts(self) -> AsyncAccountsResource:
return AsyncAccountsResource(self)
+ @cached_property
+ def bridges(self) -> AsyncBridgesResource:
+ """Manage bridge-backed account types, connections, and login sessions"""
+ from .resources.bridges import AsyncBridgesResource
+
+ return AsyncBridgesResource(self)
+
@cached_property
def chats(self) -> AsyncChatsResource:
"""Manage chats"""
@@ -485,6 +510,13 @@ def info(self) -> AsyncInfoResource:
return AsyncInfoResource(self)
+ @cached_property
+ def app(self) -> AsyncAppResource:
+ """Manage Beeper app login and encrypted messaging setup"""
+ from .resources.app import AsyncAppResource
+
+ return AsyncAppResource(self)
+
@cached_property
def with_raw_response(self) -> AsyncBeeperDesktopWithRawResponse:
return AsyncBeeperDesktopWithRawResponse(self)
@@ -500,9 +532,11 @@ def qs(self) -> Querystring:
@override
def _auth_headers(self, security: SecurityOptions) -> dict[str, str]:
- return {
- **(self._bearer_auth if security.get("bearer_auth", False) else {}),
- }
+ headers: dict[str, str] = {}
+ if security.get("bearer_auth", False):
+ for key, value in self._bearer_auth.items():
+ headers.setdefault(key, value)
+ return headers
@property
def _bearer_auth(self) -> dict[str, str]:
@@ -584,14 +618,14 @@ async def focus(
timeout: float | httpx.Timeout | None | NotGiven = not_given,
) -> FocusResponse:
"""
- Focus Beeper Desktop and optionally navigate to a specific chat, message, or
- pre-fill plain text and an image path.
+ Focus Beeper Desktop and optionally open a specific chat, jump to a message, or
+ pre-fill text and an image.
Args:
chat_id: Optional Beeper chat ID (or local chat ID) to focus after opening the app. If
omitted, only opens/focuses the app.
- draft_attachment_path: Optional image path to populate in the message input field.
+ draft_attachment_path: Optional local image path to populate in the message input field.
draft_text: Optional plain text to populate in the message input field.
@@ -634,12 +668,12 @@ async def search(
timeout: float | httpx.Timeout | None | NotGiven = not_given,
) -> SearchResponse:
"""
- Returns matching chats, participant name matches in groups, and the first page
- of messages in one call. Paginate messages via search-messages. Paginate chats
- via search-chats.
+ Return matching chats, participant matches in group chats, and the first page of
+ message results in one call. Use the dedicated chat and message search endpoints
+ for pagination.
Args:
- query: User-typed search text. Literal word matching (non-semantic).
+ query: User-typed search text. Uses literal word matching.
extra_headers: Send extra headers
@@ -715,6 +749,13 @@ def accounts(self) -> accounts.AccountsResourceWithRawResponse:
return AccountsResourceWithRawResponse(self._client.accounts)
+ @cached_property
+ def bridges(self) -> bridges.BridgesResourceWithRawResponse:
+ """Manage bridge-backed account types, connections, and login sessions"""
+ from .resources.bridges import BridgesResourceWithRawResponse
+
+ return BridgesResourceWithRawResponse(self._client.bridges)
+
@cached_property
def chats(self) -> chats.ChatsResourceWithRawResponse:
"""Manage chats"""
@@ -746,6 +787,13 @@ def info(self) -> info.InfoResourceWithRawResponse:
return InfoResourceWithRawResponse(self._client.info)
+ @cached_property
+ def app(self) -> app.AppResourceWithRawResponse:
+ """Manage Beeper app login and encrypted messaging setup"""
+ from .resources.app import AppResourceWithRawResponse
+
+ return AppResourceWithRawResponse(self._client.app)
+
class AsyncBeeperDesktopWithRawResponse:
_client: AsyncBeeperDesktop
@@ -767,6 +815,13 @@ def accounts(self) -> accounts.AsyncAccountsResourceWithRawResponse:
return AsyncAccountsResourceWithRawResponse(self._client.accounts)
+ @cached_property
+ def bridges(self) -> bridges.AsyncBridgesResourceWithRawResponse:
+ """Manage bridge-backed account types, connections, and login sessions"""
+ from .resources.bridges import AsyncBridgesResourceWithRawResponse
+
+ return AsyncBridgesResourceWithRawResponse(self._client.bridges)
+
@cached_property
def chats(self) -> chats.AsyncChatsResourceWithRawResponse:
"""Manage chats"""
@@ -798,6 +853,13 @@ def info(self) -> info.AsyncInfoResourceWithRawResponse:
return AsyncInfoResourceWithRawResponse(self._client.info)
+ @cached_property
+ def app(self) -> app.AsyncAppResourceWithRawResponse:
+ """Manage Beeper app login and encrypted messaging setup"""
+ from .resources.app import AsyncAppResourceWithRawResponse
+
+ return AsyncAppResourceWithRawResponse(self._client.app)
+
class BeeperDesktopWithStreamedResponse:
_client: BeeperDesktop
@@ -819,6 +881,13 @@ def accounts(self) -> accounts.AccountsResourceWithStreamingResponse:
return AccountsResourceWithStreamingResponse(self._client.accounts)
+ @cached_property
+ def bridges(self) -> bridges.BridgesResourceWithStreamingResponse:
+ """Manage bridge-backed account types, connections, and login sessions"""
+ from .resources.bridges import BridgesResourceWithStreamingResponse
+
+ return BridgesResourceWithStreamingResponse(self._client.bridges)
+
@cached_property
def chats(self) -> chats.ChatsResourceWithStreamingResponse:
"""Manage chats"""
@@ -850,6 +919,13 @@ def info(self) -> info.InfoResourceWithStreamingResponse:
return InfoResourceWithStreamingResponse(self._client.info)
+ @cached_property
+ def app(self) -> app.AppResourceWithStreamingResponse:
+ """Manage Beeper app login and encrypted messaging setup"""
+ from .resources.app import AppResourceWithStreamingResponse
+
+ return AppResourceWithStreamingResponse(self._client.app)
+
class AsyncBeeperDesktopWithStreamedResponse:
_client: AsyncBeeperDesktop
@@ -871,6 +947,13 @@ def accounts(self) -> accounts.AsyncAccountsResourceWithStreamingResponse:
return AsyncAccountsResourceWithStreamingResponse(self._client.accounts)
+ @cached_property
+ def bridges(self) -> bridges.AsyncBridgesResourceWithStreamingResponse:
+ """Manage bridge-backed account types, connections, and login sessions"""
+ from .resources.bridges import AsyncBridgesResourceWithStreamingResponse
+
+ return AsyncBridgesResourceWithStreamingResponse(self._client.bridges)
+
@cached_property
def chats(self) -> chats.AsyncChatsResourceWithStreamingResponse:
"""Manage chats"""
@@ -902,6 +985,13 @@ def info(self) -> info.AsyncInfoResourceWithStreamingResponse:
return AsyncInfoResourceWithStreamingResponse(self._client.info)
+ @cached_property
+ def app(self) -> app.AsyncAppResourceWithStreamingResponse:
+ """Manage Beeper app login and encrypted messaging setup"""
+ from .resources.app import AsyncAppResourceWithStreamingResponse
+
+ return AsyncAppResourceWithStreamingResponse(self._client.app)
+
Client = BeeperDesktop
diff --git a/src/beeper_desktop_api/_files.py b/src/beeper_desktop_api/_files.py
index 8a371d3..be8e0e1 100644
--- a/src/beeper_desktop_api/_files.py
+++ b/src/beeper_desktop_api/_files.py
@@ -99,7 +99,7 @@ async def async_to_httpx_files(files: RequestFiles | None) -> HttpxRequestFiles
elif is_sequence_t(files):
files = [(key, await _async_transform_file(file)) for key, file in files]
else:
- raise TypeError("Unexpected file type input {type(files)}, expected mapping or sequence")
+ raise TypeError(f"Unexpected file type input {type(files)}, expected mapping or sequence")
return files
diff --git a/src/beeper_desktop_api/_models.py b/src/beeper_desktop_api/_models.py
index e22dd2a..69f41a6 100644
--- a/src/beeper_desktop_api/_models.py
+++ b/src/beeper_desktop_api/_models.py
@@ -25,7 +25,9 @@
ClassVar,
Protocol,
Required,
+ Annotated,
ParamSpec,
+ TypeAlias,
TypedDict,
TypeGuard,
final,
@@ -79,7 +81,15 @@
from ._constants import RAW_RESPONSE_HEADER
if TYPE_CHECKING:
+ from pydantic import GetCoreSchemaHandler, ValidatorFunctionWrapHandler
+ from pydantic_core import CoreSchema, core_schema
from pydantic_core.core_schema import ModelField, ModelSchema, LiteralSchema, ModelFieldsSchema
+else:
+ try:
+ from pydantic_core import CoreSchema, core_schema
+ except ImportError:
+ CoreSchema = None
+ core_schema = None
__all__ = ["BaseModel", "GenericModel"]
@@ -396,6 +406,76 @@ def model_dump_json(
)
+class _EagerIterable(list[_T], Generic[_T]):
+ """
+ Accepts any Iterable[T] input (including generators), consumes it
+ eagerly, and validates all items upfront.
+
+ Validation preserves the original container type where possible
+ (e.g. a set[T] stays a set[T]). Serialization (model_dump / JSON)
+ always emits a list — round-tripping through model_dump() will not
+ restore the original container type.
+ """
+
+ @classmethod
+ def __get_pydantic_core_schema__(
+ cls,
+ source_type: Any,
+ handler: GetCoreSchemaHandler,
+ ) -> CoreSchema:
+ (item_type,) = get_args(source_type) or (Any,)
+ item_schema: CoreSchema = handler.generate_schema(item_type)
+ list_of_items_schema: CoreSchema = core_schema.list_schema(item_schema)
+
+ return core_schema.no_info_wrap_validator_function(
+ cls._validate,
+ list_of_items_schema,
+ serialization=core_schema.plain_serializer_function_ser_schema(
+ cls._serialize,
+ info_arg=False,
+ ),
+ )
+
+ @staticmethod
+ def _validate(v: Iterable[_T], handler: "ValidatorFunctionWrapHandler") -> Any:
+ original_type: type[Any] = type(v)
+
+ # Normalize to list so list_schema can validate each item
+ if isinstance(v, list):
+ items: list[_T] = v
+ else:
+ try:
+ items = list(v)
+ except TypeError as e:
+ raise TypeError("Value is not iterable") from e
+
+ # Validate items against the inner schema
+ validated: list[_T] = handler(items)
+
+ # Reconstruct original container type
+ if original_type is list:
+ return validated
+ # str(list) produces the list's repr, not a string built from items,
+ # so skip reconstruction for str and its subclasses.
+ if issubclass(original_type, str):
+ return validated
+ try:
+ return original_type(validated)
+ except (TypeError, ValueError):
+ # If the type cannot be reconstructed, just return the validated list
+ return validated
+
+ @staticmethod
+ def _serialize(v: Iterable[_T]) -> list[_T]:
+ """Always serialize as a list so Pydantic's JSON encoder is happy."""
+ if isinstance(v, list):
+ return v
+ return list(v)
+
+
+EagerIterable: TypeAlias = Annotated[Iterable[_T], _EagerIterable]
+
+
def _construct_field(value: object, field: FieldInfo, key: str) -> object:
if value is None:
return field_get_default(field)
diff --git a/src/beeper_desktop_api/resources/__init__.py b/src/beeper_desktop_api/resources/__init__.py
index a066e9b..528054c 100644
--- a/src/beeper_desktop_api/resources/__init__.py
+++ b/src/beeper_desktop_api/resources/__init__.py
@@ -1,5 +1,13 @@
# File generated from our OpenAPI spec by Stainless. See CONTRIBUTING.md for details.
+from .app import (
+ AppResource,
+ AsyncAppResource,
+ AppResourceWithRawResponse,
+ AsyncAppResourceWithRawResponse,
+ AppResourceWithStreamingResponse,
+ AsyncAppResourceWithStreamingResponse,
+)
from .info import (
InfoResource,
AsyncInfoResource,
@@ -24,6 +32,14 @@
AssetsResourceWithStreamingResponse,
AsyncAssetsResourceWithStreamingResponse,
)
+from .bridges import (
+ BridgesResource,
+ AsyncBridgesResource,
+ BridgesResourceWithRawResponse,
+ AsyncBridgesResourceWithRawResponse,
+ BridgesResourceWithStreamingResponse,
+ AsyncBridgesResourceWithStreamingResponse,
+)
from .accounts import (
AccountsResource,
AsyncAccountsResource,
@@ -48,6 +64,12 @@
"AsyncAccountsResourceWithRawResponse",
"AccountsResourceWithStreamingResponse",
"AsyncAccountsResourceWithStreamingResponse",
+ "BridgesResource",
+ "AsyncBridgesResource",
+ "BridgesResourceWithRawResponse",
+ "AsyncBridgesResourceWithRawResponse",
+ "BridgesResourceWithStreamingResponse",
+ "AsyncBridgesResourceWithStreamingResponse",
"ChatsResource",
"AsyncChatsResource",
"ChatsResourceWithRawResponse",
@@ -72,4 +94,10 @@
"AsyncInfoResourceWithRawResponse",
"InfoResourceWithStreamingResponse",
"AsyncInfoResourceWithStreamingResponse",
+ "AppResource",
+ "AsyncAppResource",
+ "AppResourceWithRawResponse",
+ "AsyncAppResourceWithRawResponse",
+ "AppResourceWithStreamingResponse",
+ "AsyncAppResourceWithStreamingResponse",
]
diff --git a/src/beeper_desktop_api/resources/accounts/accounts.py b/src/beeper_desktop_api/resources/accounts/accounts.py
index 15cfd33..8c247e4 100644
--- a/src/beeper_desktop_api/resources/accounts/accounts.py
+++ b/src/beeper_desktop_api/resources/accounts/accounts.py
@@ -5,6 +5,7 @@
import httpx
from ..._types import Body, Query, Headers, NotGiven, not_given
+from ..._utils import path_template
from .contacts import (
ContactsResource,
AsyncContactsResource,
@@ -23,6 +24,7 @@
)
from ..._base_client import make_request_options
from ...types.account_list_response import AccountListResponse
+from ...types.account_retrieve_response import AccountRetrieveResponse
__all__ = ["AccountsResource", "AsyncAccountsResource"]
@@ -54,6 +56,41 @@ def with_streaming_response(self) -> AccountsResourceWithStreamingResponse:
"""
return AccountsResourceWithStreamingResponse(self)
+ def retrieve(
+ self,
+ account_id: str,
+ *,
+ # Use the following arguments if you need to pass additional parameters to the API that aren't available via kwargs.
+ # The extra values given here take precedence over values defined on the client or passed to this method.
+ extra_headers: Headers | None = None,
+ extra_query: Query | None = None,
+ extra_body: Body | None = None,
+ timeout: float | httpx.Timeout | None | NotGiven = not_given,
+ ) -> AccountRetrieveResponse:
+ """
+ Get one chat account connected to this Beeper Client API server.
+
+ Args:
+ account_id: Account ID this resource belongs to.
+
+ extra_headers: Send extra headers
+
+ extra_query: Add additional query parameters to the request
+
+ extra_body: Add additional JSON properties to the request
+
+ timeout: Override the client-level default timeout for this request, in seconds
+ """
+ if not account_id:
+ raise ValueError(f"Expected a non-empty value for `account_id` but received {account_id!r}")
+ return self._get(
+ path_template("/v1/accounts/{account_id}", account_id=account_id),
+ options=make_request_options(
+ extra_headers=extra_headers, extra_query=extra_query, extra_body=extra_body, timeout=timeout
+ ),
+ cast_to=AccountRetrieveResponse,
+ )
+
def list(
self,
*,
@@ -65,8 +102,8 @@ def list(
timeout: float | httpx.Timeout | None | NotGiven = not_given,
) -> AccountListResponse:
"""
- List Chat Accounts connected to this Beeper Desktop instance, including bridge
- metadata and network identity.
+ List chat accounts connected to this Beeper Client API server, including bridge,
+ network, user identity, and connection status.
"""
return self._get(
"/v1/accounts",
@@ -104,6 +141,41 @@ def with_streaming_response(self) -> AsyncAccountsResourceWithStreamingResponse:
"""
return AsyncAccountsResourceWithStreamingResponse(self)
+ async def retrieve(
+ self,
+ account_id: str,
+ *,
+ # Use the following arguments if you need to pass additional parameters to the API that aren't available via kwargs.
+ # The extra values given here take precedence over values defined on the client or passed to this method.
+ extra_headers: Headers | None = None,
+ extra_query: Query | None = None,
+ extra_body: Body | None = None,
+ timeout: float | httpx.Timeout | None | NotGiven = not_given,
+ ) -> AccountRetrieveResponse:
+ """
+ Get one chat account connected to this Beeper Client API server.
+
+ Args:
+ account_id: Account ID this resource belongs to.
+
+ extra_headers: Send extra headers
+
+ extra_query: Add additional query parameters to the request
+
+ extra_body: Add additional JSON properties to the request
+
+ timeout: Override the client-level default timeout for this request, in seconds
+ """
+ if not account_id:
+ raise ValueError(f"Expected a non-empty value for `account_id` but received {account_id!r}")
+ return await self._get(
+ path_template("/v1/accounts/{account_id}", account_id=account_id),
+ options=make_request_options(
+ extra_headers=extra_headers, extra_query=extra_query, extra_body=extra_body, timeout=timeout
+ ),
+ cast_to=AccountRetrieveResponse,
+ )
+
async def list(
self,
*,
@@ -115,8 +187,8 @@ async def list(
timeout: float | httpx.Timeout | None | NotGiven = not_given,
) -> AccountListResponse:
"""
- List Chat Accounts connected to this Beeper Desktop instance, including bridge
- metadata and network identity.
+ List chat accounts connected to this Beeper Client API server, including bridge,
+ network, user identity, and connection status.
"""
return await self._get(
"/v1/accounts",
@@ -131,6 +203,9 @@ class AccountsResourceWithRawResponse:
def __init__(self, accounts: AccountsResource) -> None:
self._accounts = accounts
+ self.retrieve = to_raw_response_wrapper(
+ accounts.retrieve,
+ )
self.list = to_raw_response_wrapper(
accounts.list,
)
@@ -145,6 +220,9 @@ class AsyncAccountsResourceWithRawResponse:
def __init__(self, accounts: AsyncAccountsResource) -> None:
self._accounts = accounts
+ self.retrieve = async_to_raw_response_wrapper(
+ accounts.retrieve,
+ )
self.list = async_to_raw_response_wrapper(
accounts.list,
)
@@ -159,6 +237,9 @@ class AccountsResourceWithStreamingResponse:
def __init__(self, accounts: AccountsResource) -> None:
self._accounts = accounts
+ self.retrieve = to_streamed_response_wrapper(
+ accounts.retrieve,
+ )
self.list = to_streamed_response_wrapper(
accounts.list,
)
@@ -173,6 +254,9 @@ class AsyncAccountsResourceWithStreamingResponse:
def __init__(self, accounts: AsyncAccountsResource) -> None:
self._accounts = accounts
+ self.retrieve = async_to_streamed_response_wrapper(
+ accounts.retrieve,
+ )
self.list = async_to_streamed_response_wrapper(
accounts.list,
)
diff --git a/src/beeper_desktop_api/resources/accounts/contacts.py b/src/beeper_desktop_api/resources/accounts/contacts.py
index ba704bb..7c6469d 100644
--- a/src/beeper_desktop_api/resources/accounts/contacts.py
+++ b/src/beeper_desktop_api/resources/accounts/contacts.py
@@ -75,7 +75,7 @@ def list(
limit: Maximum contacts to return per page.
- query: Optional search query for blended contact lookup.
+ query: Optional search query for contact lookup.
extra_headers: Send extra headers
@@ -127,7 +127,7 @@ def search(
Args:
account_id: Account ID this resource belongs to.
- query: Text to search users by. Network-specific behavior.
+ query: Text to search contacts by. Matching behavior depends on the network.
extra_headers: Send extra headers
@@ -202,7 +202,7 @@ def list(
limit: Maximum contacts to return per page.
- query: Optional search query for blended contact lookup.
+ query: Optional search query for contact lookup.
extra_headers: Send extra headers
@@ -254,7 +254,7 @@ async def search(
Args:
account_id: Account ID this resource belongs to.
- query: Text to search users by. Network-specific behavior.
+ query: Text to search contacts by. Matching behavior depends on the network.
extra_headers: Send extra headers
diff --git a/src/beeper_desktop_api/resources/app/__init__.py b/src/beeper_desktop_api/resources/app/__init__.py
new file mode 100644
index 0000000..68b246e
--- /dev/null
+++ b/src/beeper_desktop_api/resources/app/__init__.py
@@ -0,0 +1,47 @@
+# File generated from our OpenAPI spec by Stainless. See CONTRIBUTING.md for details.
+
+from .app import (
+ AppResource,
+ AsyncAppResource,
+ AppResourceWithRawResponse,
+ AsyncAppResourceWithRawResponse,
+ AppResourceWithStreamingResponse,
+ AsyncAppResourceWithStreamingResponse,
+)
+from .login import (
+ LoginResource,
+ AsyncLoginResource,
+ LoginResourceWithRawResponse,
+ AsyncLoginResourceWithRawResponse,
+ LoginResourceWithStreamingResponse,
+ AsyncLoginResourceWithStreamingResponse,
+)
+from .verifications import (
+ VerificationsResource,
+ AsyncVerificationsResource,
+ VerificationsResourceWithRawResponse,
+ AsyncVerificationsResourceWithRawResponse,
+ VerificationsResourceWithStreamingResponse,
+ AsyncVerificationsResourceWithStreamingResponse,
+)
+
+__all__ = [
+ "LoginResource",
+ "AsyncLoginResource",
+ "LoginResourceWithRawResponse",
+ "AsyncLoginResourceWithRawResponse",
+ "LoginResourceWithStreamingResponse",
+ "AsyncLoginResourceWithStreamingResponse",
+ "VerificationsResource",
+ "AsyncVerificationsResource",
+ "VerificationsResourceWithRawResponse",
+ "AsyncVerificationsResourceWithRawResponse",
+ "VerificationsResourceWithStreamingResponse",
+ "AsyncVerificationsResourceWithStreamingResponse",
+ "AppResource",
+ "AsyncAppResource",
+ "AppResourceWithRawResponse",
+ "AsyncAppResourceWithRawResponse",
+ "AppResourceWithStreamingResponse",
+ "AsyncAppResourceWithStreamingResponse",
+]
diff --git a/src/beeper_desktop_api/resources/app/app.py b/src/beeper_desktop_api/resources/app/app.py
new file mode 100644
index 0000000..e53e47f
--- /dev/null
+++ b/src/beeper_desktop_api/resources/app/app.py
@@ -0,0 +1,223 @@
+# File generated from our OpenAPI spec by Stainless. See CONTRIBUTING.md for details.
+
+from __future__ import annotations
+
+import httpx
+
+from ..._types import Body, Query, Headers, NotGiven, not_given
+from ..._compat import cached_property
+from ..._resource import SyncAPIResource, AsyncAPIResource
+from ..._response import (
+ to_raw_response_wrapper,
+ to_streamed_response_wrapper,
+ async_to_raw_response_wrapper,
+ async_to_streamed_response_wrapper,
+)
+from .login.login import (
+ LoginResource,
+ AsyncLoginResource,
+ LoginResourceWithRawResponse,
+ AsyncLoginResourceWithRawResponse,
+ LoginResourceWithStreamingResponse,
+ AsyncLoginResourceWithStreamingResponse,
+)
+from ..._base_client import make_request_options
+from .verifications.verifications import (
+ VerificationsResource,
+ AsyncVerificationsResource,
+ VerificationsResourceWithRawResponse,
+ AsyncVerificationsResourceWithRawResponse,
+ VerificationsResourceWithStreamingResponse,
+ AsyncVerificationsResourceWithStreamingResponse,
+)
+from ...types.app_session_response import AppSessionResponse
+
+__all__ = ["AppResource", "AsyncAppResource"]
+
+
+class AppResource(SyncAPIResource):
+ """Manage Beeper app login and encrypted messaging setup"""
+
+ @cached_property
+ def login(self) -> LoginResource:
+ """Complete first-party Beeper app login"""
+ return LoginResource(self._client)
+
+ @cached_property
+ def verifications(self) -> VerificationsResource:
+ """Manage device verification transactions"""
+ return VerificationsResource(self._client)
+
+ @cached_property
+ def with_raw_response(self) -> AppResourceWithRawResponse:
+ """
+ This property can be used as a prefix for any HTTP method call to return
+ the raw response object instead of the parsed content.
+
+ For more information, see https://www.github.com/beeper/desktop-api-python#accessing-raw-response-data-eg-headers
+ """
+ return AppResourceWithRawResponse(self)
+
+ @cached_property
+ def with_streaming_response(self) -> AppResourceWithStreamingResponse:
+ """
+ An alternative to `.with_raw_response` that doesn't eagerly read the response body.
+
+ For more information, see https://www.github.com/beeper/desktop-api-python#with_streaming_response
+ """
+ return AppResourceWithStreamingResponse(self)
+
+ def session(
+ self,
+ *,
+ # Use the following arguments if you need to pass additional parameters to the API that aren't available via kwargs.
+ # The extra values given here take precedence over values defined on the client or passed to this method.
+ extra_headers: Headers | None = None,
+ extra_query: Query | None = None,
+ extra_body: Body | None = None,
+ timeout: float | httpx.Timeout | None | NotGiven = not_given,
+ ) -> AppSessionResponse:
+ """
+ Return the current Beeper Desktop or Beeper Server sign-in and encrypted
+ messaging setup state. This endpoint is public before sign-in so apps can
+ discover that sign-in is needed; after sign-in, pass a read token.
+ """
+ return self._get(
+ "/v1/app/setup",
+ options=make_request_options(
+ extra_headers=extra_headers, extra_query=extra_query, extra_body=extra_body, timeout=timeout
+ ),
+ cast_to=AppSessionResponse,
+ )
+
+
+class AsyncAppResource(AsyncAPIResource):
+ """Manage Beeper app login and encrypted messaging setup"""
+
+ @cached_property
+ def login(self) -> AsyncLoginResource:
+ """Complete first-party Beeper app login"""
+ return AsyncLoginResource(self._client)
+
+ @cached_property
+ def verifications(self) -> AsyncVerificationsResource:
+ """Manage device verification transactions"""
+ return AsyncVerificationsResource(self._client)
+
+ @cached_property
+ def with_raw_response(self) -> AsyncAppResourceWithRawResponse:
+ """
+ This property can be used as a prefix for any HTTP method call to return
+ the raw response object instead of the parsed content.
+
+ For more information, see https://www.github.com/beeper/desktop-api-python#accessing-raw-response-data-eg-headers
+ """
+ return AsyncAppResourceWithRawResponse(self)
+
+ @cached_property
+ def with_streaming_response(self) -> AsyncAppResourceWithStreamingResponse:
+ """
+ An alternative to `.with_raw_response` that doesn't eagerly read the response body.
+
+ For more information, see https://www.github.com/beeper/desktop-api-python#with_streaming_response
+ """
+ return AsyncAppResourceWithStreamingResponse(self)
+
+ async def session(
+ self,
+ *,
+ # Use the following arguments if you need to pass additional parameters to the API that aren't available via kwargs.
+ # The extra values given here take precedence over values defined on the client or passed to this method.
+ extra_headers: Headers | None = None,
+ extra_query: Query | None = None,
+ extra_body: Body | None = None,
+ timeout: float | httpx.Timeout | None | NotGiven = not_given,
+ ) -> AppSessionResponse:
+ """
+ Return the current Beeper Desktop or Beeper Server sign-in and encrypted
+ messaging setup state. This endpoint is public before sign-in so apps can
+ discover that sign-in is needed; after sign-in, pass a read token.
+ """
+ return await self._get(
+ "/v1/app/setup",
+ options=make_request_options(
+ extra_headers=extra_headers, extra_query=extra_query, extra_body=extra_body, timeout=timeout
+ ),
+ cast_to=AppSessionResponse,
+ )
+
+
+class AppResourceWithRawResponse:
+ def __init__(self, app: AppResource) -> None:
+ self._app = app
+
+ self.session = to_raw_response_wrapper(
+ app.session,
+ )
+
+ @cached_property
+ def login(self) -> LoginResourceWithRawResponse:
+ """Complete first-party Beeper app login"""
+ return LoginResourceWithRawResponse(self._app.login)
+
+ @cached_property
+ def verifications(self) -> VerificationsResourceWithRawResponse:
+ """Manage device verification transactions"""
+ return VerificationsResourceWithRawResponse(self._app.verifications)
+
+
+class AsyncAppResourceWithRawResponse:
+ def __init__(self, app: AsyncAppResource) -> None:
+ self._app = app
+
+ self.session = async_to_raw_response_wrapper(
+ app.session,
+ )
+
+ @cached_property
+ def login(self) -> AsyncLoginResourceWithRawResponse:
+ """Complete first-party Beeper app login"""
+ return AsyncLoginResourceWithRawResponse(self._app.login)
+
+ @cached_property
+ def verifications(self) -> AsyncVerificationsResourceWithRawResponse:
+ """Manage device verification transactions"""
+ return AsyncVerificationsResourceWithRawResponse(self._app.verifications)
+
+
+class AppResourceWithStreamingResponse:
+ def __init__(self, app: AppResource) -> None:
+ self._app = app
+
+ self.session = to_streamed_response_wrapper(
+ app.session,
+ )
+
+ @cached_property
+ def login(self) -> LoginResourceWithStreamingResponse:
+ """Complete first-party Beeper app login"""
+ return LoginResourceWithStreamingResponse(self._app.login)
+
+ @cached_property
+ def verifications(self) -> VerificationsResourceWithStreamingResponse:
+ """Manage device verification transactions"""
+ return VerificationsResourceWithStreamingResponse(self._app.verifications)
+
+
+class AsyncAppResourceWithStreamingResponse:
+ def __init__(self, app: AsyncAppResource) -> None:
+ self._app = app
+
+ self.session = async_to_streamed_response_wrapper(
+ app.session,
+ )
+
+ @cached_property
+ def login(self) -> AsyncLoginResourceWithStreamingResponse:
+ """Complete first-party Beeper app login"""
+ return AsyncLoginResourceWithStreamingResponse(self._app.login)
+
+ @cached_property
+ def verifications(self) -> AsyncVerificationsResourceWithStreamingResponse:
+ """Manage device verification transactions"""
+ return AsyncVerificationsResourceWithStreamingResponse(self._app.verifications)
diff --git a/src/beeper_desktop_api/resources/app/login/__init__.py b/src/beeper_desktop_api/resources/app/login/__init__.py
new file mode 100644
index 0000000..fa69ee9
--- /dev/null
+++ b/src/beeper_desktop_api/resources/app/login/__init__.py
@@ -0,0 +1,33 @@
+# File generated from our OpenAPI spec by Stainless. See CONTRIBUTING.md for details.
+
+from .login import (
+ LoginResource,
+ AsyncLoginResource,
+ LoginResourceWithRawResponse,
+ AsyncLoginResourceWithRawResponse,
+ LoginResourceWithStreamingResponse,
+ AsyncLoginResourceWithStreamingResponse,
+)
+from .verification import (
+ VerificationResource,
+ AsyncVerificationResource,
+ VerificationResourceWithRawResponse,
+ AsyncVerificationResourceWithRawResponse,
+ VerificationResourceWithStreamingResponse,
+ AsyncVerificationResourceWithStreamingResponse,
+)
+
+__all__ = [
+ "VerificationResource",
+ "AsyncVerificationResource",
+ "VerificationResourceWithRawResponse",
+ "AsyncVerificationResourceWithRawResponse",
+ "VerificationResourceWithStreamingResponse",
+ "AsyncVerificationResourceWithStreamingResponse",
+ "LoginResource",
+ "AsyncLoginResource",
+ "LoginResourceWithRawResponse",
+ "AsyncLoginResourceWithRawResponse",
+ "LoginResourceWithStreamingResponse",
+ "AsyncLoginResourceWithStreamingResponse",
+]
diff --git a/src/beeper_desktop_api/resources/app/login/login.py b/src/beeper_desktop_api/resources/app/login/login.py
new file mode 100644
index 0000000..ed63a26
--- /dev/null
+++ b/src/beeper_desktop_api/resources/app/login/login.py
@@ -0,0 +1,552 @@
+# File generated from our OpenAPI spec by Stainless. See CONTRIBUTING.md for details.
+
+from __future__ import annotations
+
+from typing import Any, cast
+from typing_extensions import Literal
+
+import httpx
+
+from ...._types import Body, Query, Headers, NoneType, NotGiven, not_given
+from ...._utils import maybe_transform, async_maybe_transform
+from ...._compat import cached_property
+from ...._resource import SyncAPIResource, AsyncAPIResource
+from ...._response import (
+ to_raw_response_wrapper,
+ to_streamed_response_wrapper,
+ async_to_raw_response_wrapper,
+ async_to_streamed_response_wrapper,
+)
+from ....types.app import login_email_params, login_register_params, login_response_params
+from ...._base_client import make_request_options
+from .verification.verification import (
+ VerificationResource,
+ AsyncVerificationResource,
+ VerificationResourceWithRawResponse,
+ AsyncVerificationResourceWithRawResponse,
+ VerificationResourceWithStreamingResponse,
+ AsyncVerificationResourceWithStreamingResponse,
+)
+from ....types.app.login_start_response import LoginStartResponse
+from ....types.app.login_register_response import LoginRegisterResponse
+from ....types.app.login_response_response import LoginResponseResponse
+
+__all__ = ["LoginResource", "AsyncLoginResource"]
+
+
+class LoginResource(SyncAPIResource):
+ """Complete first-party Beeper app login"""
+
+ @cached_property
+ def verification(self) -> VerificationResource:
+ return VerificationResource(self._client)
+
+ @cached_property
+ def with_raw_response(self) -> LoginResourceWithRawResponse:
+ """
+ This property can be used as a prefix for any HTTP method call to return
+ the raw response object instead of the parsed content.
+
+ For more information, see https://www.github.com/beeper/desktop-api-python#accessing-raw-response-data-eg-headers
+ """
+ return LoginResourceWithRawResponse(self)
+
+ @cached_property
+ def with_streaming_response(self) -> LoginResourceWithStreamingResponse:
+ """
+ An alternative to `.with_raw_response` that doesn't eagerly read the response body.
+
+ For more information, see https://www.github.com/beeper/desktop-api-python#with_streaming_response
+ """
+ return LoginResourceWithStreamingResponse(self)
+
+ def email(
+ self,
+ *,
+ email: str,
+ setup_request_id: str,
+ # Use the following arguments if you need to pass additional parameters to the API that aren't available via kwargs.
+ # The extra values given here take precedence over values defined on the client or passed to this method.
+ extra_headers: Headers | None = None,
+ extra_query: Query | None = None,
+ extra_body: Body | None = None,
+ timeout: float | httpx.Timeout | None | NotGiven = not_given,
+ ) -> None:
+ """
+ Send a sign-in code to the user email address for app setup.
+
+ Args:
+ email: Email address to send the sign-in code to.
+
+ setup_request_id: Setup request ID returned by the start step.
+
+ extra_headers: Send extra headers
+
+ extra_query: Add additional query parameters to the request
+
+ extra_body: Add additional JSON properties to the request
+
+ timeout: Override the client-level default timeout for this request, in seconds
+ """
+ extra_headers = {"Accept": "*/*", **(extra_headers or {})}
+ return self._post(
+ "/v1/app/setup/email",
+ body=maybe_transform(
+ {
+ "email": email,
+ "setup_request_id": setup_request_id,
+ },
+ login_email_params.LoginEmailParams,
+ ),
+ options=make_request_options(
+ extra_headers=extra_headers,
+ extra_query=extra_query,
+ extra_body=extra_body,
+ timeout=timeout,
+ security={},
+ ),
+ cast_to=NoneType,
+ )
+
+ def register(
+ self,
+ *,
+ accept_terms: Literal[True],
+ lead_token: str,
+ setup_request_id: str,
+ username: str,
+ # Use the following arguments if you need to pass additional parameters to the API that aren't available via kwargs.
+ # The extra values given here take precedence over values defined on the client or passed to this method.
+ extra_headers: Headers | None = None,
+ extra_query: Query | None = None,
+ extra_body: Body | None = None,
+ timeout: float | httpx.Timeout | None | NotGiven = not_given,
+ ) -> LoginRegisterResponse:
+ """
+ Create a Beeper account after the user chooses a username and accepts the Terms
+ of Use.
+
+ Args:
+ accept_terms: Confirms that the user agreed to our
+ [terms of use](https://www.beeper.com/terms-onboarding) and has read our
+ [privacy policy](https://www.beeper.com/privacy).
+
+ lead_token: Registration token returned by Beeper.
+
+ setup_request_id: Setup request ID returned by the start step.
+
+ username: Username selected by the user.
+
+ extra_headers: Send extra headers
+
+ extra_query: Add additional query parameters to the request
+
+ extra_body: Add additional JSON properties to the request
+
+ timeout: Override the client-level default timeout for this request, in seconds
+ """
+ return self._post(
+ "/v1/app/setup/register",
+ body=maybe_transform(
+ {
+ "accept_terms": accept_terms,
+ "lead_token": lead_token,
+ "setup_request_id": setup_request_id,
+ "username": username,
+ },
+ login_register_params.LoginRegisterParams,
+ ),
+ options=make_request_options(
+ extra_headers=extra_headers,
+ extra_query=extra_query,
+ extra_body=extra_body,
+ timeout=timeout,
+ security={},
+ ),
+ cast_to=LoginRegisterResponse,
+ )
+
+ def response(
+ self,
+ *,
+ response: str,
+ setup_request_id: str,
+ # Use the following arguments if you need to pass additional parameters to the API that aren't available via kwargs.
+ # The extra values given here take precedence over values defined on the client or passed to this method.
+ extra_headers: Headers | None = None,
+ extra_query: Query | None = None,
+ extra_body: Body | None = None,
+ timeout: float | httpx.Timeout | None | NotGiven = not_given,
+ ) -> LoginResponseResponse:
+ """Finish setup sign-in with the code sent to the user email address.
+
+ If the user
+ needs a new account, the response includes account creation copy and username
+ suggestions.
+
+ Args:
+ response: Sign-in code from the user email.
+
+ setup_request_id: Setup request ID returned by the start step.
+
+ extra_headers: Send extra headers
+
+ extra_query: Add additional query parameters to the request
+
+ extra_body: Add additional JSON properties to the request
+
+ timeout: Override the client-level default timeout for this request, in seconds
+ """
+ return cast(
+ LoginResponseResponse,
+ self._post(
+ "/v1/app/setup/response",
+ body=maybe_transform(
+ {
+ "response": response,
+ "setup_request_id": setup_request_id,
+ },
+ login_response_params.LoginResponseParams,
+ ),
+ options=make_request_options(
+ extra_headers=extra_headers,
+ extra_query=extra_query,
+ extra_body=extra_body,
+ timeout=timeout,
+ security={},
+ ),
+ cast_to=cast(
+ Any, LoginResponseResponse
+ ), # Union types cannot be passed in as arguments in the type system
+ ),
+ )
+
+ def start(
+ self,
+ *,
+ # Use the following arguments if you need to pass additional parameters to the API that aren't available via kwargs.
+ # The extra values given here take precedence over values defined on the client or passed to this method.
+ extra_headers: Headers | None = None,
+ extra_query: Query | None = None,
+ extra_body: Body | None = None,
+ timeout: float | httpx.Timeout | None | NotGiven = not_given,
+ ) -> LoginStartResponse:
+ """Start setting up Beeper Desktop or Beeper Server.
+
+ The flow supports existing
+ Beeper accounts and new account creation.
+ """
+ return self._post(
+ "/v1/app/setup/start",
+ options=make_request_options(
+ extra_headers=extra_headers,
+ extra_query=extra_query,
+ extra_body=extra_body,
+ timeout=timeout,
+ security={},
+ ),
+ cast_to=LoginStartResponse,
+ )
+
+
+class AsyncLoginResource(AsyncAPIResource):
+ """Complete first-party Beeper app login"""
+
+ @cached_property
+ def verification(self) -> AsyncVerificationResource:
+ return AsyncVerificationResource(self._client)
+
+ @cached_property
+ def with_raw_response(self) -> AsyncLoginResourceWithRawResponse:
+ """
+ This property can be used as a prefix for any HTTP method call to return
+ the raw response object instead of the parsed content.
+
+ For more information, see https://www.github.com/beeper/desktop-api-python#accessing-raw-response-data-eg-headers
+ """
+ return AsyncLoginResourceWithRawResponse(self)
+
+ @cached_property
+ def with_streaming_response(self) -> AsyncLoginResourceWithStreamingResponse:
+ """
+ An alternative to `.with_raw_response` that doesn't eagerly read the response body.
+
+ For more information, see https://www.github.com/beeper/desktop-api-python#with_streaming_response
+ """
+ return AsyncLoginResourceWithStreamingResponse(self)
+
+ async def email(
+ self,
+ *,
+ email: str,
+ setup_request_id: str,
+ # Use the following arguments if you need to pass additional parameters to the API that aren't available via kwargs.
+ # The extra values given here take precedence over values defined on the client or passed to this method.
+ extra_headers: Headers | None = None,
+ extra_query: Query | None = None,
+ extra_body: Body | None = None,
+ timeout: float | httpx.Timeout | None | NotGiven = not_given,
+ ) -> None:
+ """
+ Send a sign-in code to the user email address for app setup.
+
+ Args:
+ email: Email address to send the sign-in code to.
+
+ setup_request_id: Setup request ID returned by the start step.
+
+ extra_headers: Send extra headers
+
+ extra_query: Add additional query parameters to the request
+
+ extra_body: Add additional JSON properties to the request
+
+ timeout: Override the client-level default timeout for this request, in seconds
+ """
+ extra_headers = {"Accept": "*/*", **(extra_headers or {})}
+ return await self._post(
+ "/v1/app/setup/email",
+ body=await async_maybe_transform(
+ {
+ "email": email,
+ "setup_request_id": setup_request_id,
+ },
+ login_email_params.LoginEmailParams,
+ ),
+ options=make_request_options(
+ extra_headers=extra_headers,
+ extra_query=extra_query,
+ extra_body=extra_body,
+ timeout=timeout,
+ security={},
+ ),
+ cast_to=NoneType,
+ )
+
+ async def register(
+ self,
+ *,
+ accept_terms: Literal[True],
+ lead_token: str,
+ setup_request_id: str,
+ username: str,
+ # Use the following arguments if you need to pass additional parameters to the API that aren't available via kwargs.
+ # The extra values given here take precedence over values defined on the client or passed to this method.
+ extra_headers: Headers | None = None,
+ extra_query: Query | None = None,
+ extra_body: Body | None = None,
+ timeout: float | httpx.Timeout | None | NotGiven = not_given,
+ ) -> LoginRegisterResponse:
+ """
+ Create a Beeper account after the user chooses a username and accepts the Terms
+ of Use.
+
+ Args:
+ accept_terms: Confirms that the user agreed to our
+ [terms of use](https://www.beeper.com/terms-onboarding) and has read our
+ [privacy policy](https://www.beeper.com/privacy).
+
+ lead_token: Registration token returned by Beeper.
+
+ setup_request_id: Setup request ID returned by the start step.
+
+ username: Username selected by the user.
+
+ extra_headers: Send extra headers
+
+ extra_query: Add additional query parameters to the request
+
+ extra_body: Add additional JSON properties to the request
+
+ timeout: Override the client-level default timeout for this request, in seconds
+ """
+ return await self._post(
+ "/v1/app/setup/register",
+ body=await async_maybe_transform(
+ {
+ "accept_terms": accept_terms,
+ "lead_token": lead_token,
+ "setup_request_id": setup_request_id,
+ "username": username,
+ },
+ login_register_params.LoginRegisterParams,
+ ),
+ options=make_request_options(
+ extra_headers=extra_headers,
+ extra_query=extra_query,
+ extra_body=extra_body,
+ timeout=timeout,
+ security={},
+ ),
+ cast_to=LoginRegisterResponse,
+ )
+
+ async def response(
+ self,
+ *,
+ response: str,
+ setup_request_id: str,
+ # Use the following arguments if you need to pass additional parameters to the API that aren't available via kwargs.
+ # The extra values given here take precedence over values defined on the client or passed to this method.
+ extra_headers: Headers | None = None,
+ extra_query: Query | None = None,
+ extra_body: Body | None = None,
+ timeout: float | httpx.Timeout | None | NotGiven = not_given,
+ ) -> LoginResponseResponse:
+ """Finish setup sign-in with the code sent to the user email address.
+
+ If the user
+ needs a new account, the response includes account creation copy and username
+ suggestions.
+
+ Args:
+ response: Sign-in code from the user email.
+
+ setup_request_id: Setup request ID returned by the start step.
+
+ extra_headers: Send extra headers
+
+ extra_query: Add additional query parameters to the request
+
+ extra_body: Add additional JSON properties to the request
+
+ timeout: Override the client-level default timeout for this request, in seconds
+ """
+ return cast(
+ LoginResponseResponse,
+ await self._post(
+ "/v1/app/setup/response",
+ body=await async_maybe_transform(
+ {
+ "response": response,
+ "setup_request_id": setup_request_id,
+ },
+ login_response_params.LoginResponseParams,
+ ),
+ options=make_request_options(
+ extra_headers=extra_headers,
+ extra_query=extra_query,
+ extra_body=extra_body,
+ timeout=timeout,
+ security={},
+ ),
+ cast_to=cast(
+ Any, LoginResponseResponse
+ ), # Union types cannot be passed in as arguments in the type system
+ ),
+ )
+
+ async def start(
+ self,
+ *,
+ # Use the following arguments if you need to pass additional parameters to the API that aren't available via kwargs.
+ # The extra values given here take precedence over values defined on the client or passed to this method.
+ extra_headers: Headers | None = None,
+ extra_query: Query | None = None,
+ extra_body: Body | None = None,
+ timeout: float | httpx.Timeout | None | NotGiven = not_given,
+ ) -> LoginStartResponse:
+ """Start setting up Beeper Desktop or Beeper Server.
+
+ The flow supports existing
+ Beeper accounts and new account creation.
+ """
+ return await self._post(
+ "/v1/app/setup/start",
+ options=make_request_options(
+ extra_headers=extra_headers,
+ extra_query=extra_query,
+ extra_body=extra_body,
+ timeout=timeout,
+ security={},
+ ),
+ cast_to=LoginStartResponse,
+ )
+
+
+class LoginResourceWithRawResponse:
+ def __init__(self, login: LoginResource) -> None:
+ self._login = login
+
+ self.email = to_raw_response_wrapper(
+ login.email,
+ )
+ self.register = to_raw_response_wrapper(
+ login.register,
+ )
+ self.response = to_raw_response_wrapper(
+ login.response,
+ )
+ self.start = to_raw_response_wrapper(
+ login.start,
+ )
+
+ @cached_property
+ def verification(self) -> VerificationResourceWithRawResponse:
+ return VerificationResourceWithRawResponse(self._login.verification)
+
+
+class AsyncLoginResourceWithRawResponse:
+ def __init__(self, login: AsyncLoginResource) -> None:
+ self._login = login
+
+ self.email = async_to_raw_response_wrapper(
+ login.email,
+ )
+ self.register = async_to_raw_response_wrapper(
+ login.register,
+ )
+ self.response = async_to_raw_response_wrapper(
+ login.response,
+ )
+ self.start = async_to_raw_response_wrapper(
+ login.start,
+ )
+
+ @cached_property
+ def verification(self) -> AsyncVerificationResourceWithRawResponse:
+ return AsyncVerificationResourceWithRawResponse(self._login.verification)
+
+
+class LoginResourceWithStreamingResponse:
+ def __init__(self, login: LoginResource) -> None:
+ self._login = login
+
+ self.email = to_streamed_response_wrapper(
+ login.email,
+ )
+ self.register = to_streamed_response_wrapper(
+ login.register,
+ )
+ self.response = to_streamed_response_wrapper(
+ login.response,
+ )
+ self.start = to_streamed_response_wrapper(
+ login.start,
+ )
+
+ @cached_property
+ def verification(self) -> VerificationResourceWithStreamingResponse:
+ return VerificationResourceWithStreamingResponse(self._login.verification)
+
+
+class AsyncLoginResourceWithStreamingResponse:
+ def __init__(self, login: AsyncLoginResource) -> None:
+ self._login = login
+
+ self.email = async_to_streamed_response_wrapper(
+ login.email,
+ )
+ self.register = async_to_streamed_response_wrapper(
+ login.register,
+ )
+ self.response = async_to_streamed_response_wrapper(
+ login.response,
+ )
+ self.start = async_to_streamed_response_wrapper(
+ login.start,
+ )
+
+ @cached_property
+ def verification(self) -> AsyncVerificationResourceWithStreamingResponse:
+ return AsyncVerificationResourceWithStreamingResponse(self._login.verification)
diff --git a/src/beeper_desktop_api/resources/app/login/verification/__init__.py b/src/beeper_desktop_api/resources/app/login/verification/__init__.py
new file mode 100644
index 0000000..05bb4b1
--- /dev/null
+++ b/src/beeper_desktop_api/resources/app/login/verification/__init__.py
@@ -0,0 +1,33 @@
+# File generated from our OpenAPI spec by Stainless. See CONTRIBUTING.md for details.
+
+from .recovery_key import (
+ RecoveryKeyResource,
+ AsyncRecoveryKeyResource,
+ RecoveryKeyResourceWithRawResponse,
+ AsyncRecoveryKeyResourceWithRawResponse,
+ RecoveryKeyResourceWithStreamingResponse,
+ AsyncRecoveryKeyResourceWithStreamingResponse,
+)
+from .verification import (
+ VerificationResource,
+ AsyncVerificationResource,
+ VerificationResourceWithRawResponse,
+ AsyncVerificationResourceWithRawResponse,
+ VerificationResourceWithStreamingResponse,
+ AsyncVerificationResourceWithStreamingResponse,
+)
+
+__all__ = [
+ "RecoveryKeyResource",
+ "AsyncRecoveryKeyResource",
+ "RecoveryKeyResourceWithRawResponse",
+ "AsyncRecoveryKeyResourceWithRawResponse",
+ "RecoveryKeyResourceWithStreamingResponse",
+ "AsyncRecoveryKeyResourceWithStreamingResponse",
+ "VerificationResource",
+ "AsyncVerificationResource",
+ "VerificationResourceWithRawResponse",
+ "AsyncVerificationResourceWithRawResponse",
+ "VerificationResourceWithStreamingResponse",
+ "AsyncVerificationResourceWithStreamingResponse",
+]
diff --git a/src/beeper_desktop_api/resources/app/login/verification/recovery_key/__init__.py b/src/beeper_desktop_api/resources/app/login/verification/recovery_key/__init__.py
new file mode 100644
index 0000000..ec6a210
--- /dev/null
+++ b/src/beeper_desktop_api/resources/app/login/verification/recovery_key/__init__.py
@@ -0,0 +1,33 @@
+# File generated from our OpenAPI spec by Stainless. See CONTRIBUTING.md for details.
+
+from .reset import (
+ ResetResource,
+ AsyncResetResource,
+ ResetResourceWithRawResponse,
+ AsyncResetResourceWithRawResponse,
+ ResetResourceWithStreamingResponse,
+ AsyncResetResourceWithStreamingResponse,
+)
+from .recovery_key import (
+ RecoveryKeyResource,
+ AsyncRecoveryKeyResource,
+ RecoveryKeyResourceWithRawResponse,
+ AsyncRecoveryKeyResourceWithRawResponse,
+ RecoveryKeyResourceWithStreamingResponse,
+ AsyncRecoveryKeyResourceWithStreamingResponse,
+)
+
+__all__ = [
+ "ResetResource",
+ "AsyncResetResource",
+ "ResetResourceWithRawResponse",
+ "AsyncResetResourceWithRawResponse",
+ "ResetResourceWithStreamingResponse",
+ "AsyncResetResourceWithStreamingResponse",
+ "RecoveryKeyResource",
+ "AsyncRecoveryKeyResource",
+ "RecoveryKeyResourceWithRawResponse",
+ "AsyncRecoveryKeyResourceWithRawResponse",
+ "RecoveryKeyResourceWithStreamingResponse",
+ "AsyncRecoveryKeyResourceWithStreamingResponse",
+]
diff --git a/src/beeper_desktop_api/resources/app/login/verification/recovery_key/recovery_key.py b/src/beeper_desktop_api/resources/app/login/verification/recovery_key/recovery_key.py
new file mode 100644
index 0000000..ba347a5
--- /dev/null
+++ b/src/beeper_desktop_api/resources/app/login/verification/recovery_key/recovery_key.py
@@ -0,0 +1,227 @@
+# File generated from our OpenAPI spec by Stainless. See CONTRIBUTING.md for details.
+
+from __future__ import annotations
+
+import httpx
+
+from .reset import (
+ ResetResource,
+ AsyncResetResource,
+ ResetResourceWithRawResponse,
+ AsyncResetResourceWithRawResponse,
+ ResetResourceWithStreamingResponse,
+ AsyncResetResourceWithStreamingResponse,
+)
+from ......_types import Body, Query, Headers, NotGiven, not_given
+from ......_utils import maybe_transform, async_maybe_transform
+from ......_compat import cached_property
+from ......_resource import SyncAPIResource, AsyncAPIResource
+from ......_response import (
+ to_raw_response_wrapper,
+ to_streamed_response_wrapper,
+ async_to_raw_response_wrapper,
+ async_to_streamed_response_wrapper,
+)
+from ......_base_client import make_request_options
+from ......types.app.login.verification import recovery_key_verify_params
+from ......types.app.login.verification.recovery_key_verify_response import RecoveryKeyVerifyResponse
+
+__all__ = ["RecoveryKeyResource", "AsyncRecoveryKeyResource"]
+
+
+class RecoveryKeyResource(SyncAPIResource):
+ """
+ First-party sign-in and encrypted messaging setup for Beeper Desktop and Beeper Server.
+ """
+
+ @cached_property
+ def reset(self) -> ResetResource:
+ """
+ First-party sign-in and encrypted messaging setup for Beeper Desktop and Beeper Server.
+ """
+ return ResetResource(self._client)
+
+ @cached_property
+ def with_raw_response(self) -> RecoveryKeyResourceWithRawResponse:
+ """
+ This property can be used as a prefix for any HTTP method call to return
+ the raw response object instead of the parsed content.
+
+ For more information, see https://www.github.com/beeper/desktop-api-python#accessing-raw-response-data-eg-headers
+ """
+ return RecoveryKeyResourceWithRawResponse(self)
+
+ @cached_property
+ def with_streaming_response(self) -> RecoveryKeyResourceWithStreamingResponse:
+ """
+ An alternative to `.with_raw_response` that doesn't eagerly read the response body.
+
+ For more information, see https://www.github.com/beeper/desktop-api-python#with_streaming_response
+ """
+ return RecoveryKeyResourceWithStreamingResponse(self)
+
+ def verify(
+ self,
+ *,
+ recovery_key: str,
+ # Use the following arguments if you need to pass additional parameters to the API that aren't available via kwargs.
+ # The extra values given here take precedence over values defined on the client or passed to this method.
+ extra_headers: Headers | None = None,
+ extra_query: Query | None = None,
+ extra_body: Body | None = None,
+ timeout: float | httpx.Timeout | None | NotGiven = not_given,
+ ) -> RecoveryKeyVerifyResponse:
+ """
+ Unlock encrypted messages with the user recovery key.
+
+ Args:
+ recovery_key: Recovery key saved by the user.
+
+ extra_headers: Send extra headers
+
+ extra_query: Add additional query parameters to the request
+
+ extra_body: Add additional JSON properties to the request
+
+ timeout: Override the client-level default timeout for this request, in seconds
+ """
+ return self._post(
+ "/v1/app/setup/verification/recovery-key",
+ body=maybe_transform({"recovery_key": recovery_key}, recovery_key_verify_params.RecoveryKeyVerifyParams),
+ options=make_request_options(
+ extra_headers=extra_headers, extra_query=extra_query, extra_body=extra_body, timeout=timeout
+ ),
+ cast_to=RecoveryKeyVerifyResponse,
+ )
+
+
+class AsyncRecoveryKeyResource(AsyncAPIResource):
+ """
+ First-party sign-in and encrypted messaging setup for Beeper Desktop and Beeper Server.
+ """
+
+ @cached_property
+ def reset(self) -> AsyncResetResource:
+ """
+ First-party sign-in and encrypted messaging setup for Beeper Desktop and Beeper Server.
+ """
+ return AsyncResetResource(self._client)
+
+ @cached_property
+ def with_raw_response(self) -> AsyncRecoveryKeyResourceWithRawResponse:
+ """
+ This property can be used as a prefix for any HTTP method call to return
+ the raw response object instead of the parsed content.
+
+ For more information, see https://www.github.com/beeper/desktop-api-python#accessing-raw-response-data-eg-headers
+ """
+ return AsyncRecoveryKeyResourceWithRawResponse(self)
+
+ @cached_property
+ def with_streaming_response(self) -> AsyncRecoveryKeyResourceWithStreamingResponse:
+ """
+ An alternative to `.with_raw_response` that doesn't eagerly read the response body.
+
+ For more information, see https://www.github.com/beeper/desktop-api-python#with_streaming_response
+ """
+ return AsyncRecoveryKeyResourceWithStreamingResponse(self)
+
+ async def verify(
+ self,
+ *,
+ recovery_key: str,
+ # Use the following arguments if you need to pass additional parameters to the API that aren't available via kwargs.
+ # The extra values given here take precedence over values defined on the client or passed to this method.
+ extra_headers: Headers | None = None,
+ extra_query: Query | None = None,
+ extra_body: Body | None = None,
+ timeout: float | httpx.Timeout | None | NotGiven = not_given,
+ ) -> RecoveryKeyVerifyResponse:
+ """
+ Unlock encrypted messages with the user recovery key.
+
+ Args:
+ recovery_key: Recovery key saved by the user.
+
+ extra_headers: Send extra headers
+
+ extra_query: Add additional query parameters to the request
+
+ extra_body: Add additional JSON properties to the request
+
+ timeout: Override the client-level default timeout for this request, in seconds
+ """
+ return await self._post(
+ "/v1/app/setup/verification/recovery-key",
+ body=await async_maybe_transform(
+ {"recovery_key": recovery_key}, recovery_key_verify_params.RecoveryKeyVerifyParams
+ ),
+ options=make_request_options(
+ extra_headers=extra_headers, extra_query=extra_query, extra_body=extra_body, timeout=timeout
+ ),
+ cast_to=RecoveryKeyVerifyResponse,
+ )
+
+
+class RecoveryKeyResourceWithRawResponse:
+ def __init__(self, recovery_key: RecoveryKeyResource) -> None:
+ self._recovery_key = recovery_key
+
+ self.verify = to_raw_response_wrapper(
+ recovery_key.verify,
+ )
+
+ @cached_property
+ def reset(self) -> ResetResourceWithRawResponse:
+ """
+ First-party sign-in and encrypted messaging setup for Beeper Desktop and Beeper Server.
+ """
+ return ResetResourceWithRawResponse(self._recovery_key.reset)
+
+
+class AsyncRecoveryKeyResourceWithRawResponse:
+ def __init__(self, recovery_key: AsyncRecoveryKeyResource) -> None:
+ self._recovery_key = recovery_key
+
+ self.verify = async_to_raw_response_wrapper(
+ recovery_key.verify,
+ )
+
+ @cached_property
+ def reset(self) -> AsyncResetResourceWithRawResponse:
+ """
+ First-party sign-in and encrypted messaging setup for Beeper Desktop and Beeper Server.
+ """
+ return AsyncResetResourceWithRawResponse(self._recovery_key.reset)
+
+
+class RecoveryKeyResourceWithStreamingResponse:
+ def __init__(self, recovery_key: RecoveryKeyResource) -> None:
+ self._recovery_key = recovery_key
+
+ self.verify = to_streamed_response_wrapper(
+ recovery_key.verify,
+ )
+
+ @cached_property
+ def reset(self) -> ResetResourceWithStreamingResponse:
+ """
+ First-party sign-in and encrypted messaging setup for Beeper Desktop and Beeper Server.
+ """
+ return ResetResourceWithStreamingResponse(self._recovery_key.reset)
+
+
+class AsyncRecoveryKeyResourceWithStreamingResponse:
+ def __init__(self, recovery_key: AsyncRecoveryKeyResource) -> None:
+ self._recovery_key = recovery_key
+
+ self.verify = async_to_streamed_response_wrapper(
+ recovery_key.verify,
+ )
+
+ @cached_property
+ def reset(self) -> AsyncResetResourceWithStreamingResponse:
+ """
+ First-party sign-in and encrypted messaging setup for Beeper Desktop and Beeper Server.
+ """
+ return AsyncResetResourceWithStreamingResponse(self._recovery_key.reset)
diff --git a/src/beeper_desktop_api/resources/app/login/verification/recovery_key/reset.py b/src/beeper_desktop_api/resources/app/login/verification/recovery_key/reset.py
new file mode 100644
index 0000000..0272f0f
--- /dev/null
+++ b/src/beeper_desktop_api/resources/app/login/verification/recovery_key/reset.py
@@ -0,0 +1,260 @@
+# File generated from our OpenAPI spec by Stainless. See CONTRIBUTING.md for details.
+
+from __future__ import annotations
+
+import httpx
+
+from ......_types import Body, Omit, Query, Headers, NotGiven, omit, not_given
+from ......_utils import maybe_transform, async_maybe_transform
+from ......_compat import cached_property
+from ......_resource import SyncAPIResource, AsyncAPIResource
+from ......_response import (
+ to_raw_response_wrapper,
+ to_streamed_response_wrapper,
+ async_to_raw_response_wrapper,
+ async_to_streamed_response_wrapper,
+)
+from ......_base_client import make_request_options
+from ......types.app.login.verification.recovery_key import reset_create_params, reset_confirm_params
+from ......types.app.login.verification.recovery_key.reset_create_response import ResetCreateResponse
+from ......types.app.login.verification.recovery_key.reset_confirm_response import ResetConfirmResponse
+
+__all__ = ["ResetResource", "AsyncResetResource"]
+
+
+class ResetResource(SyncAPIResource):
+ """
+ First-party sign-in and encrypted messaging setup for Beeper Desktop and Beeper Server.
+ """
+
+ @cached_property
+ def with_raw_response(self) -> ResetResourceWithRawResponse:
+ """
+ This property can be used as a prefix for any HTTP method call to return
+ the raw response object instead of the parsed content.
+
+ For more information, see https://www.github.com/beeper/desktop-api-python#accessing-raw-response-data-eg-headers
+ """
+ return ResetResourceWithRawResponse(self)
+
+ @cached_property
+ def with_streaming_response(self) -> ResetResourceWithStreamingResponse:
+ """
+ An alternative to `.with_raw_response` that doesn't eagerly read the response body.
+
+ For more information, see https://www.github.com/beeper/desktop-api-python#with_streaming_response
+ """
+ return ResetResourceWithStreamingResponse(self)
+
+ def create(
+ self,
+ *,
+ existing_recovery_key: str | Omit = omit,
+ # Use the following arguments if you need to pass additional parameters to the API that aren't available via kwargs.
+ # The extra values given here take precedence over values defined on the client or passed to this method.
+ extra_headers: Headers | None = None,
+ extra_query: Query | None = None,
+ extra_body: Body | None = None,
+ timeout: float | httpx.Timeout | None | NotGiven = not_given,
+ ) -> ResetCreateResponse:
+ """
+ Create a new recovery key when the user cannot use the existing one.
+
+ Args:
+ existing_recovery_key: Existing recovery key, if the user has it.
+
+ extra_headers: Send extra headers
+
+ extra_query: Add additional query parameters to the request
+
+ extra_body: Add additional JSON properties to the request
+
+ timeout: Override the client-level default timeout for this request, in seconds
+ """
+ return self._post(
+ "/v1/app/setup/verification/recovery-key/reset",
+ body=maybe_transform(
+ {"existing_recovery_key": existing_recovery_key}, reset_create_params.ResetCreateParams
+ ),
+ options=make_request_options(
+ extra_headers=extra_headers, extra_query=extra_query, extra_body=extra_body, timeout=timeout
+ ),
+ cast_to=ResetCreateResponse,
+ )
+
+ def confirm(
+ self,
+ *,
+ recovery_key: str,
+ # Use the following arguments if you need to pass additional parameters to the API that aren't available via kwargs.
+ # The extra values given here take precedence over values defined on the client or passed to this method.
+ extra_headers: Headers | None = None,
+ extra_query: Query | None = None,
+ extra_body: Body | None = None,
+ timeout: float | httpx.Timeout | None | NotGiven = not_given,
+ ) -> ResetConfirmResponse:
+ """
+ Confirm that the new recovery key should be used for this account.
+
+ Args:
+ recovery_key: New recovery key returned by the reset step.
+
+ extra_headers: Send extra headers
+
+ extra_query: Add additional query parameters to the request
+
+ extra_body: Add additional JSON properties to the request
+
+ timeout: Override the client-level default timeout for this request, in seconds
+ """
+ return self._post(
+ "/v1/app/setup/verification/recovery-key/reset/confirm",
+ body=maybe_transform({"recovery_key": recovery_key}, reset_confirm_params.ResetConfirmParams),
+ options=make_request_options(
+ extra_headers=extra_headers, extra_query=extra_query, extra_body=extra_body, timeout=timeout
+ ),
+ cast_to=ResetConfirmResponse,
+ )
+
+
+class AsyncResetResource(AsyncAPIResource):
+ """
+ First-party sign-in and encrypted messaging setup for Beeper Desktop and Beeper Server.
+ """
+
+ @cached_property
+ def with_raw_response(self) -> AsyncResetResourceWithRawResponse:
+ """
+ This property can be used as a prefix for any HTTP method call to return
+ the raw response object instead of the parsed content.
+
+ For more information, see https://www.github.com/beeper/desktop-api-python#accessing-raw-response-data-eg-headers
+ """
+ return AsyncResetResourceWithRawResponse(self)
+
+ @cached_property
+ def with_streaming_response(self) -> AsyncResetResourceWithStreamingResponse:
+ """
+ An alternative to `.with_raw_response` that doesn't eagerly read the response body.
+
+ For more information, see https://www.github.com/beeper/desktop-api-python#with_streaming_response
+ """
+ return AsyncResetResourceWithStreamingResponse(self)
+
+ async def create(
+ self,
+ *,
+ existing_recovery_key: str | Omit = omit,
+ # Use the following arguments if you need to pass additional parameters to the API that aren't available via kwargs.
+ # The extra values given here take precedence over values defined on the client or passed to this method.
+ extra_headers: Headers | None = None,
+ extra_query: Query | None = None,
+ extra_body: Body | None = None,
+ timeout: float | httpx.Timeout | None | NotGiven = not_given,
+ ) -> ResetCreateResponse:
+ """
+ Create a new recovery key when the user cannot use the existing one.
+
+ Args:
+ existing_recovery_key: Existing recovery key, if the user has it.
+
+ extra_headers: Send extra headers
+
+ extra_query: Add additional query parameters to the request
+
+ extra_body: Add additional JSON properties to the request
+
+ timeout: Override the client-level default timeout for this request, in seconds
+ """
+ return await self._post(
+ "/v1/app/setup/verification/recovery-key/reset",
+ body=await async_maybe_transform(
+ {"existing_recovery_key": existing_recovery_key}, reset_create_params.ResetCreateParams
+ ),
+ options=make_request_options(
+ extra_headers=extra_headers, extra_query=extra_query, extra_body=extra_body, timeout=timeout
+ ),
+ cast_to=ResetCreateResponse,
+ )
+
+ async def confirm(
+ self,
+ *,
+ recovery_key: str,
+ # Use the following arguments if you need to pass additional parameters to the API that aren't available via kwargs.
+ # The extra values given here take precedence over values defined on the client or passed to this method.
+ extra_headers: Headers | None = None,
+ extra_query: Query | None = None,
+ extra_body: Body | None = None,
+ timeout: float | httpx.Timeout | None | NotGiven = not_given,
+ ) -> ResetConfirmResponse:
+ """
+ Confirm that the new recovery key should be used for this account.
+
+ Args:
+ recovery_key: New recovery key returned by the reset step.
+
+ extra_headers: Send extra headers
+
+ extra_query: Add additional query parameters to the request
+
+ extra_body: Add additional JSON properties to the request
+
+ timeout: Override the client-level default timeout for this request, in seconds
+ """
+ return await self._post(
+ "/v1/app/setup/verification/recovery-key/reset/confirm",
+ body=await async_maybe_transform({"recovery_key": recovery_key}, reset_confirm_params.ResetConfirmParams),
+ options=make_request_options(
+ extra_headers=extra_headers, extra_query=extra_query, extra_body=extra_body, timeout=timeout
+ ),
+ cast_to=ResetConfirmResponse,
+ )
+
+
+class ResetResourceWithRawResponse:
+ def __init__(self, reset: ResetResource) -> None:
+ self._reset = reset
+
+ self.create = to_raw_response_wrapper(
+ reset.create,
+ )
+ self.confirm = to_raw_response_wrapper(
+ reset.confirm,
+ )
+
+
+class AsyncResetResourceWithRawResponse:
+ def __init__(self, reset: AsyncResetResource) -> None:
+ self._reset = reset
+
+ self.create = async_to_raw_response_wrapper(
+ reset.create,
+ )
+ self.confirm = async_to_raw_response_wrapper(
+ reset.confirm,
+ )
+
+
+class ResetResourceWithStreamingResponse:
+ def __init__(self, reset: ResetResource) -> None:
+ self._reset = reset
+
+ self.create = to_streamed_response_wrapper(
+ reset.create,
+ )
+ self.confirm = to_streamed_response_wrapper(
+ reset.confirm,
+ )
+
+
+class AsyncResetResourceWithStreamingResponse:
+ def __init__(self, reset: AsyncResetResource) -> None:
+ self._reset = reset
+
+ self.create = async_to_streamed_response_wrapper(
+ reset.create,
+ )
+ self.confirm = async_to_streamed_response_wrapper(
+ reset.confirm,
+ )
diff --git a/src/beeper_desktop_api/resources/app/login/verification/verification.py b/src/beeper_desktop_api/resources/app/login/verification/verification.py
new file mode 100644
index 0000000..9c940ce
--- /dev/null
+++ b/src/beeper_desktop_api/resources/app/login/verification/verification.py
@@ -0,0 +1,120 @@
+# File generated from our OpenAPI spec by Stainless. See CONTRIBUTING.md for details.
+
+from __future__ import annotations
+
+from ....._compat import cached_property
+from ....._resource import SyncAPIResource, AsyncAPIResource
+from .recovery_key.recovery_key import (
+ RecoveryKeyResource,
+ AsyncRecoveryKeyResource,
+ RecoveryKeyResourceWithRawResponse,
+ AsyncRecoveryKeyResourceWithRawResponse,
+ RecoveryKeyResourceWithStreamingResponse,
+ AsyncRecoveryKeyResourceWithStreamingResponse,
+)
+
+__all__ = ["VerificationResource", "AsyncVerificationResource"]
+
+
+class VerificationResource(SyncAPIResource):
+ @cached_property
+ def recovery_key(self) -> RecoveryKeyResource:
+ """
+ First-party sign-in and encrypted messaging setup for Beeper Desktop and Beeper Server.
+ """
+ return RecoveryKeyResource(self._client)
+
+ @cached_property
+ def with_raw_response(self) -> VerificationResourceWithRawResponse:
+ """
+ This property can be used as a prefix for any HTTP method call to return
+ the raw response object instead of the parsed content.
+
+ For more information, see https://www.github.com/beeper/desktop-api-python#accessing-raw-response-data-eg-headers
+ """
+ return VerificationResourceWithRawResponse(self)
+
+ @cached_property
+ def with_streaming_response(self) -> VerificationResourceWithStreamingResponse:
+ """
+ An alternative to `.with_raw_response` that doesn't eagerly read the response body.
+
+ For more information, see https://www.github.com/beeper/desktop-api-python#with_streaming_response
+ """
+ return VerificationResourceWithStreamingResponse(self)
+
+
+class AsyncVerificationResource(AsyncAPIResource):
+ @cached_property
+ def recovery_key(self) -> AsyncRecoveryKeyResource:
+ """
+ First-party sign-in and encrypted messaging setup for Beeper Desktop and Beeper Server.
+ """
+ return AsyncRecoveryKeyResource(self._client)
+
+ @cached_property
+ def with_raw_response(self) -> AsyncVerificationResourceWithRawResponse:
+ """
+ This property can be used as a prefix for any HTTP method call to return
+ the raw response object instead of the parsed content.
+
+ For more information, see https://www.github.com/beeper/desktop-api-python#accessing-raw-response-data-eg-headers
+ """
+ return AsyncVerificationResourceWithRawResponse(self)
+
+ @cached_property
+ def with_streaming_response(self) -> AsyncVerificationResourceWithStreamingResponse:
+ """
+ An alternative to `.with_raw_response` that doesn't eagerly read the response body.
+
+ For more information, see https://www.github.com/beeper/desktop-api-python#with_streaming_response
+ """
+ return AsyncVerificationResourceWithStreamingResponse(self)
+
+
+class VerificationResourceWithRawResponse:
+ def __init__(self, verification: VerificationResource) -> None:
+ self._verification = verification
+
+ @cached_property
+ def recovery_key(self) -> RecoveryKeyResourceWithRawResponse:
+ """
+ First-party sign-in and encrypted messaging setup for Beeper Desktop and Beeper Server.
+ """
+ return RecoveryKeyResourceWithRawResponse(self._verification.recovery_key)
+
+
+class AsyncVerificationResourceWithRawResponse:
+ def __init__(self, verification: AsyncVerificationResource) -> None:
+ self._verification = verification
+
+ @cached_property
+ def recovery_key(self) -> AsyncRecoveryKeyResourceWithRawResponse:
+ """
+ First-party sign-in and encrypted messaging setup for Beeper Desktop and Beeper Server.
+ """
+ return AsyncRecoveryKeyResourceWithRawResponse(self._verification.recovery_key)
+
+
+class VerificationResourceWithStreamingResponse:
+ def __init__(self, verification: VerificationResource) -> None:
+ self._verification = verification
+
+ @cached_property
+ def recovery_key(self) -> RecoveryKeyResourceWithStreamingResponse:
+ """
+ First-party sign-in and encrypted messaging setup for Beeper Desktop and Beeper Server.
+ """
+ return RecoveryKeyResourceWithStreamingResponse(self._verification.recovery_key)
+
+
+class AsyncVerificationResourceWithStreamingResponse:
+ def __init__(self, verification: AsyncVerificationResource) -> None:
+ self._verification = verification
+
+ @cached_property
+ def recovery_key(self) -> AsyncRecoveryKeyResourceWithStreamingResponse:
+ """
+ First-party sign-in and encrypted messaging setup for Beeper Desktop and Beeper Server.
+ """
+ return AsyncRecoveryKeyResourceWithStreamingResponse(self._verification.recovery_key)
diff --git a/src/beeper_desktop_api/resources/app/verifications/__init__.py b/src/beeper_desktop_api/resources/app/verifications/__init__.py
new file mode 100644
index 0000000..b57241e
--- /dev/null
+++ b/src/beeper_desktop_api/resources/app/verifications/__init__.py
@@ -0,0 +1,47 @@
+# File generated from our OpenAPI spec by Stainless. See CONTRIBUTING.md for details.
+
+from .qr import (
+ QrResource,
+ AsyncQrResource,
+ QrResourceWithRawResponse,
+ AsyncQrResourceWithRawResponse,
+ QrResourceWithStreamingResponse,
+ AsyncQrResourceWithStreamingResponse,
+)
+from .sas import (
+ SASResource,
+ AsyncSASResource,
+ SASResourceWithRawResponse,
+ AsyncSASResourceWithRawResponse,
+ SASResourceWithStreamingResponse,
+ AsyncSASResourceWithStreamingResponse,
+)
+from .verifications import (
+ VerificationsResource,
+ AsyncVerificationsResource,
+ VerificationsResourceWithRawResponse,
+ AsyncVerificationsResourceWithRawResponse,
+ VerificationsResourceWithStreamingResponse,
+ AsyncVerificationsResourceWithStreamingResponse,
+)
+
+__all__ = [
+ "QrResource",
+ "AsyncQrResource",
+ "QrResourceWithRawResponse",
+ "AsyncQrResourceWithRawResponse",
+ "QrResourceWithStreamingResponse",
+ "AsyncQrResourceWithStreamingResponse",
+ "SASResource",
+ "AsyncSASResource",
+ "SASResourceWithRawResponse",
+ "AsyncSASResourceWithRawResponse",
+ "SASResourceWithStreamingResponse",
+ "AsyncSASResourceWithStreamingResponse",
+ "VerificationsResource",
+ "AsyncVerificationsResource",
+ "VerificationsResourceWithRawResponse",
+ "AsyncVerificationsResourceWithRawResponse",
+ "VerificationsResourceWithStreamingResponse",
+ "AsyncVerificationsResourceWithStreamingResponse",
+]
diff --git a/src/beeper_desktop_api/resources/app/verifications/qr.py b/src/beeper_desktop_api/resources/app/verifications/qr.py
new file mode 100644
index 0000000..d01a234
--- /dev/null
+++ b/src/beeper_desktop_api/resources/app/verifications/qr.py
@@ -0,0 +1,262 @@
+# File generated from our OpenAPI spec by Stainless. See CONTRIBUTING.md for details.
+
+from __future__ import annotations
+
+import httpx
+
+from ...._types import Body, Query, Headers, NotGiven, not_given
+from ...._utils import path_template, maybe_transform, async_maybe_transform
+from ...._compat import cached_property
+from ...._resource import SyncAPIResource, AsyncAPIResource
+from ...._response import (
+ to_raw_response_wrapper,
+ to_streamed_response_wrapper,
+ async_to_raw_response_wrapper,
+ async_to_streamed_response_wrapper,
+)
+from ...._base_client import make_request_options
+from ....types.app.verifications import qr_scan_params
+from ....types.app.verifications.qr_scan_response import QrScanResponse
+from ....types.app.verifications.qr_confirm_scanned_response import QrConfirmScannedResponse
+
+__all__ = ["QrResource", "AsyncQrResource"]
+
+
+class QrResource(SyncAPIResource):
+ """
+ First-party sign-in and encrypted messaging setup for Beeper Desktop and Beeper Server.
+ """
+
+ @cached_property
+ def with_raw_response(self) -> QrResourceWithRawResponse:
+ """
+ This property can be used as a prefix for any HTTP method call to return
+ the raw response object instead of the parsed content.
+
+ For more information, see https://www.github.com/beeper/desktop-api-python#accessing-raw-response-data-eg-headers
+ """
+ return QrResourceWithRawResponse(self)
+
+ @cached_property
+ def with_streaming_response(self) -> QrResourceWithStreamingResponse:
+ """
+ An alternative to `.with_raw_response` that doesn't eagerly read the response body.
+
+ For more information, see https://www.github.com/beeper/desktop-api-python#with_streaming_response
+ """
+ return QrResourceWithStreamingResponse(self)
+
+ def confirm_scanned(
+ self,
+ verification_id: str,
+ *,
+ # Use the following arguments if you need to pass additional parameters to the API that aren't available via kwargs.
+ # The extra values given here take precedence over values defined on the client or passed to this method.
+ extra_headers: Headers | None = None,
+ extra_query: Query | None = None,
+ extra_body: Body | None = None,
+ timeout: float | httpx.Timeout | None | NotGiven = not_given,
+ ) -> QrConfirmScannedResponse:
+ """
+ Confirm that another device scanned this device QR code.
+
+ Args:
+ verification_id: Verification ID.
+
+ extra_headers: Send extra headers
+
+ extra_query: Add additional query parameters to the request
+
+ extra_body: Add additional JSON properties to the request
+
+ timeout: Override the client-level default timeout for this request, in seconds
+ """
+ if not verification_id:
+ raise ValueError(f"Expected a non-empty value for `verification_id` but received {verification_id!r}")
+ return self._post(
+ path_template(
+ "/v1/app/setup/verifications/{verification_id}/qr/confirm-scanned", verification_id=verification_id
+ ),
+ options=make_request_options(
+ extra_headers=extra_headers, extra_query=extra_query, extra_body=extra_body, timeout=timeout
+ ),
+ cast_to=QrConfirmScannedResponse,
+ )
+
+ def scan(
+ self,
+ *,
+ data: str,
+ # Use the following arguments if you need to pass additional parameters to the API that aren't available via kwargs.
+ # The extra values given here take precedence over values defined on the client or passed to this method.
+ extra_headers: Headers | None = None,
+ extra_query: Query | None = None,
+ extra_body: Body | None = None,
+ timeout: float | httpx.Timeout | None | NotGiven = not_given,
+ ) -> QrScanResponse:
+ """
+ Submit the QR code scanned from another signed-in device.
+
+ Args:
+ data: QR code payload scanned from the other device.
+
+ extra_headers: Send extra headers
+
+ extra_query: Add additional query parameters to the request
+
+ extra_body: Add additional JSON properties to the request
+
+ timeout: Override the client-level default timeout for this request, in seconds
+ """
+ return self._post(
+ "/v1/app/setup/verifications/qr/scan",
+ body=maybe_transform({"data": data}, qr_scan_params.QrScanParams),
+ options=make_request_options(
+ extra_headers=extra_headers, extra_query=extra_query, extra_body=extra_body, timeout=timeout
+ ),
+ cast_to=QrScanResponse,
+ )
+
+
+class AsyncQrResource(AsyncAPIResource):
+ """
+ First-party sign-in and encrypted messaging setup for Beeper Desktop and Beeper Server.
+ """
+
+ @cached_property
+ def with_raw_response(self) -> AsyncQrResourceWithRawResponse:
+ """
+ This property can be used as a prefix for any HTTP method call to return
+ the raw response object instead of the parsed content.
+
+ For more information, see https://www.github.com/beeper/desktop-api-python#accessing-raw-response-data-eg-headers
+ """
+ return AsyncQrResourceWithRawResponse(self)
+
+ @cached_property
+ def with_streaming_response(self) -> AsyncQrResourceWithStreamingResponse:
+ """
+ An alternative to `.with_raw_response` that doesn't eagerly read the response body.
+
+ For more information, see https://www.github.com/beeper/desktop-api-python#with_streaming_response
+ """
+ return AsyncQrResourceWithStreamingResponse(self)
+
+ async def confirm_scanned(
+ self,
+ verification_id: str,
+ *,
+ # Use the following arguments if you need to pass additional parameters to the API that aren't available via kwargs.
+ # The extra values given here take precedence over values defined on the client or passed to this method.
+ extra_headers: Headers | None = None,
+ extra_query: Query | None = None,
+ extra_body: Body | None = None,
+ timeout: float | httpx.Timeout | None | NotGiven = not_given,
+ ) -> QrConfirmScannedResponse:
+ """
+ Confirm that another device scanned this device QR code.
+
+ Args:
+ verification_id: Verification ID.
+
+ extra_headers: Send extra headers
+
+ extra_query: Add additional query parameters to the request
+
+ extra_body: Add additional JSON properties to the request
+
+ timeout: Override the client-level default timeout for this request, in seconds
+ """
+ if not verification_id:
+ raise ValueError(f"Expected a non-empty value for `verification_id` but received {verification_id!r}")
+ return await self._post(
+ path_template(
+ "/v1/app/setup/verifications/{verification_id}/qr/confirm-scanned", verification_id=verification_id
+ ),
+ options=make_request_options(
+ extra_headers=extra_headers, extra_query=extra_query, extra_body=extra_body, timeout=timeout
+ ),
+ cast_to=QrConfirmScannedResponse,
+ )
+
+ async def scan(
+ self,
+ *,
+ data: str,
+ # Use the following arguments if you need to pass additional parameters to the API that aren't available via kwargs.
+ # The extra values given here take precedence over values defined on the client or passed to this method.
+ extra_headers: Headers | None = None,
+ extra_query: Query | None = None,
+ extra_body: Body | None = None,
+ timeout: float | httpx.Timeout | None | NotGiven = not_given,
+ ) -> QrScanResponse:
+ """
+ Submit the QR code scanned from another signed-in device.
+
+ Args:
+ data: QR code payload scanned from the other device.
+
+ extra_headers: Send extra headers
+
+ extra_query: Add additional query parameters to the request
+
+ extra_body: Add additional JSON properties to the request
+
+ timeout: Override the client-level default timeout for this request, in seconds
+ """
+ return await self._post(
+ "/v1/app/setup/verifications/qr/scan",
+ body=await async_maybe_transform({"data": data}, qr_scan_params.QrScanParams),
+ options=make_request_options(
+ extra_headers=extra_headers, extra_query=extra_query, extra_body=extra_body, timeout=timeout
+ ),
+ cast_to=QrScanResponse,
+ )
+
+
+class QrResourceWithRawResponse:
+ def __init__(self, qr: QrResource) -> None:
+ self._qr = qr
+
+ self.confirm_scanned = to_raw_response_wrapper(
+ qr.confirm_scanned,
+ )
+ self.scan = to_raw_response_wrapper(
+ qr.scan,
+ )
+
+
+class AsyncQrResourceWithRawResponse:
+ def __init__(self, qr: AsyncQrResource) -> None:
+ self._qr = qr
+
+ self.confirm_scanned = async_to_raw_response_wrapper(
+ qr.confirm_scanned,
+ )
+ self.scan = async_to_raw_response_wrapper(
+ qr.scan,
+ )
+
+
+class QrResourceWithStreamingResponse:
+ def __init__(self, qr: QrResource) -> None:
+ self._qr = qr
+
+ self.confirm_scanned = to_streamed_response_wrapper(
+ qr.confirm_scanned,
+ )
+ self.scan = to_streamed_response_wrapper(
+ qr.scan,
+ )
+
+
+class AsyncQrResourceWithStreamingResponse:
+ def __init__(self, qr: AsyncQrResource) -> None:
+ self._qr = qr
+
+ self.confirm_scanned = async_to_streamed_response_wrapper(
+ qr.confirm_scanned,
+ )
+ self.scan = async_to_streamed_response_wrapper(
+ qr.scan,
+ )
diff --git a/src/beeper_desktop_api/resources/app/verifications/sas.py b/src/beeper_desktop_api/resources/app/verifications/sas.py
new file mode 100644
index 0000000..f5e5143
--- /dev/null
+++ b/src/beeper_desktop_api/resources/app/verifications/sas.py
@@ -0,0 +1,259 @@
+# File generated from our OpenAPI spec by Stainless. See CONTRIBUTING.md for details.
+
+from __future__ import annotations
+
+import httpx
+
+from ...._types import Body, Query, Headers, NotGiven, not_given
+from ...._utils import path_template
+from ...._compat import cached_property
+from ...._resource import SyncAPIResource, AsyncAPIResource
+from ...._response import (
+ to_raw_response_wrapper,
+ to_streamed_response_wrapper,
+ async_to_raw_response_wrapper,
+ async_to_streamed_response_wrapper,
+)
+from ...._base_client import make_request_options
+from ....types.app.verifications.sas_start_response import SASStartResponse
+from ....types.app.verifications.sas_confirm_response import SASConfirmResponse
+
+__all__ = ["SASResource", "AsyncSASResource"]
+
+
+class SASResource(SyncAPIResource):
+ """
+ First-party sign-in and encrypted messaging setup for Beeper Desktop and Beeper Server.
+ """
+
+ @cached_property
+ def with_raw_response(self) -> SASResourceWithRawResponse:
+ """
+ This property can be used as a prefix for any HTTP method call to return
+ the raw response object instead of the parsed content.
+
+ For more information, see https://www.github.com/beeper/desktop-api-python#accessing-raw-response-data-eg-headers
+ """
+ return SASResourceWithRawResponse(self)
+
+ @cached_property
+ def with_streaming_response(self) -> SASResourceWithStreamingResponse:
+ """
+ An alternative to `.with_raw_response` that doesn't eagerly read the response body.
+
+ For more information, see https://www.github.com/beeper/desktop-api-python#with_streaming_response
+ """
+ return SASResourceWithStreamingResponse(self)
+
+ def confirm(
+ self,
+ verification_id: str,
+ *,
+ # Use the following arguments if you need to pass additional parameters to the API that aren't available via kwargs.
+ # The extra values given here take precedence over values defined on the client or passed to this method.
+ extra_headers: Headers | None = None,
+ extra_query: Query | None = None,
+ extra_body: Body | None = None,
+ timeout: float | httpx.Timeout | None | NotGiven = not_given,
+ ) -> SASConfirmResponse:
+ """
+ Confirm that the emoji or number sequence matches on both devices.
+
+ Args:
+ verification_id: Verification ID.
+
+ extra_headers: Send extra headers
+
+ extra_query: Add additional query parameters to the request
+
+ extra_body: Add additional JSON properties to the request
+
+ timeout: Override the client-level default timeout for this request, in seconds
+ """
+ if not verification_id:
+ raise ValueError(f"Expected a non-empty value for `verification_id` but received {verification_id!r}")
+ return self._post(
+ path_template("/v1/app/setup/verifications/{verification_id}/sas/confirm", verification_id=verification_id),
+ options=make_request_options(
+ extra_headers=extra_headers, extra_query=extra_query, extra_body=extra_body, timeout=timeout
+ ),
+ cast_to=SASConfirmResponse,
+ )
+
+ def start(
+ self,
+ verification_id: str,
+ *,
+ # Use the following arguments if you need to pass additional parameters to the API that aren't available via kwargs.
+ # The extra values given here take precedence over values defined on the client or passed to this method.
+ extra_headers: Headers | None = None,
+ extra_query: Query | None = None,
+ extra_body: Body | None = None,
+ timeout: float | httpx.Timeout | None | NotGiven = not_given,
+ ) -> SASStartResponse:
+ """
+ Start emoji comparison for device verification.
+
+ Args:
+ verification_id: Verification ID.
+
+ extra_headers: Send extra headers
+
+ extra_query: Add additional query parameters to the request
+
+ extra_body: Add additional JSON properties to the request
+
+ timeout: Override the client-level default timeout for this request, in seconds
+ """
+ if not verification_id:
+ raise ValueError(f"Expected a non-empty value for `verification_id` but received {verification_id!r}")
+ return self._post(
+ path_template("/v1/app/setup/verifications/{verification_id}/sas/start", verification_id=verification_id),
+ options=make_request_options(
+ extra_headers=extra_headers, extra_query=extra_query, extra_body=extra_body, timeout=timeout
+ ),
+ cast_to=SASStartResponse,
+ )
+
+
+class AsyncSASResource(AsyncAPIResource):
+ """
+ First-party sign-in and encrypted messaging setup for Beeper Desktop and Beeper Server.
+ """
+
+ @cached_property
+ def with_raw_response(self) -> AsyncSASResourceWithRawResponse:
+ """
+ This property can be used as a prefix for any HTTP method call to return
+ the raw response object instead of the parsed content.
+
+ For more information, see https://www.github.com/beeper/desktop-api-python#accessing-raw-response-data-eg-headers
+ """
+ return AsyncSASResourceWithRawResponse(self)
+
+ @cached_property
+ def with_streaming_response(self) -> AsyncSASResourceWithStreamingResponse:
+ """
+ An alternative to `.with_raw_response` that doesn't eagerly read the response body.
+
+ For more information, see https://www.github.com/beeper/desktop-api-python#with_streaming_response
+ """
+ return AsyncSASResourceWithStreamingResponse(self)
+
+ async def confirm(
+ self,
+ verification_id: str,
+ *,
+ # Use the following arguments if you need to pass additional parameters to the API that aren't available via kwargs.
+ # The extra values given here take precedence over values defined on the client or passed to this method.
+ extra_headers: Headers | None = None,
+ extra_query: Query | None = None,
+ extra_body: Body | None = None,
+ timeout: float | httpx.Timeout | None | NotGiven = not_given,
+ ) -> SASConfirmResponse:
+ """
+ Confirm that the emoji or number sequence matches on both devices.
+
+ Args:
+ verification_id: Verification ID.
+
+ extra_headers: Send extra headers
+
+ extra_query: Add additional query parameters to the request
+
+ extra_body: Add additional JSON properties to the request
+
+ timeout: Override the client-level default timeout for this request, in seconds
+ """
+ if not verification_id:
+ raise ValueError(f"Expected a non-empty value for `verification_id` but received {verification_id!r}")
+ return await self._post(
+ path_template("/v1/app/setup/verifications/{verification_id}/sas/confirm", verification_id=verification_id),
+ options=make_request_options(
+ extra_headers=extra_headers, extra_query=extra_query, extra_body=extra_body, timeout=timeout
+ ),
+ cast_to=SASConfirmResponse,
+ )
+
+ async def start(
+ self,
+ verification_id: str,
+ *,
+ # Use the following arguments if you need to pass additional parameters to the API that aren't available via kwargs.
+ # The extra values given here take precedence over values defined on the client or passed to this method.
+ extra_headers: Headers | None = None,
+ extra_query: Query | None = None,
+ extra_body: Body | None = None,
+ timeout: float | httpx.Timeout | None | NotGiven = not_given,
+ ) -> SASStartResponse:
+ """
+ Start emoji comparison for device verification.
+
+ Args:
+ verification_id: Verification ID.
+
+ extra_headers: Send extra headers
+
+ extra_query: Add additional query parameters to the request
+
+ extra_body: Add additional JSON properties to the request
+
+ timeout: Override the client-level default timeout for this request, in seconds
+ """
+ if not verification_id:
+ raise ValueError(f"Expected a non-empty value for `verification_id` but received {verification_id!r}")
+ return await self._post(
+ path_template("/v1/app/setup/verifications/{verification_id}/sas/start", verification_id=verification_id),
+ options=make_request_options(
+ extra_headers=extra_headers, extra_query=extra_query, extra_body=extra_body, timeout=timeout
+ ),
+ cast_to=SASStartResponse,
+ )
+
+
+class SASResourceWithRawResponse:
+ def __init__(self, sas: SASResource) -> None:
+ self._sas = sas
+
+ self.confirm = to_raw_response_wrapper(
+ sas.confirm,
+ )
+ self.start = to_raw_response_wrapper(
+ sas.start,
+ )
+
+
+class AsyncSASResourceWithRawResponse:
+ def __init__(self, sas: AsyncSASResource) -> None:
+ self._sas = sas
+
+ self.confirm = async_to_raw_response_wrapper(
+ sas.confirm,
+ )
+ self.start = async_to_raw_response_wrapper(
+ sas.start,
+ )
+
+
+class SASResourceWithStreamingResponse:
+ def __init__(self, sas: SASResource) -> None:
+ self._sas = sas
+
+ self.confirm = to_streamed_response_wrapper(
+ sas.confirm,
+ )
+ self.start = to_streamed_response_wrapper(
+ sas.start,
+ )
+
+
+class AsyncSASResourceWithStreamingResponse:
+ def __init__(self, sas: AsyncSASResource) -> None:
+ self._sas = sas
+
+ self.confirm = async_to_streamed_response_wrapper(
+ sas.confirm,
+ )
+ self.start = async_to_streamed_response_wrapper(
+ sas.start,
+ )
diff --git a/src/beeper_desktop_api/resources/app/verifications/verifications.py b/src/beeper_desktop_api/resources/app/verifications/verifications.py
new file mode 100644
index 0000000..3854036
--- /dev/null
+++ b/src/beeper_desktop_api/resources/app/verifications/verifications.py
@@ -0,0 +1,625 @@
+# File generated from our OpenAPI spec by Stainless. See CONTRIBUTING.md for details.
+
+from __future__ import annotations
+
+from typing_extensions import Literal
+
+import httpx
+
+from .qr import (
+ QrResource,
+ AsyncQrResource,
+ QrResourceWithRawResponse,
+ AsyncQrResourceWithRawResponse,
+ QrResourceWithStreamingResponse,
+ AsyncQrResourceWithStreamingResponse,
+)
+from .sas import (
+ SASResource,
+ AsyncSASResource,
+ SASResourceWithRawResponse,
+ AsyncSASResourceWithRawResponse,
+ SASResourceWithStreamingResponse,
+ AsyncSASResourceWithStreamingResponse,
+)
+from ...._types import Body, Omit, Query, Headers, NotGiven, omit, not_given
+from ...._utils import path_template, maybe_transform, async_maybe_transform
+from ...._compat import cached_property
+from ...._resource import SyncAPIResource, AsyncAPIResource
+from ...._response import (
+ to_raw_response_wrapper,
+ to_streamed_response_wrapper,
+ async_to_raw_response_wrapper,
+ async_to_streamed_response_wrapper,
+)
+from ....types.app import verification_cancel_params, verification_create_params
+from ...._base_client import make_request_options
+from ....types.app.verification_list_response import VerificationListResponse
+from ....types.app.verification_accept_response import VerificationAcceptResponse
+from ....types.app.verification_cancel_response import VerificationCancelResponse
+from ....types.app.verification_create_response import VerificationCreateResponse
+from ....types.app.verification_retrieve_response import VerificationRetrieveResponse
+
+__all__ = ["VerificationsResource", "AsyncVerificationsResource"]
+
+
+class VerificationsResource(SyncAPIResource):
+ """Manage device verification transactions"""
+
+ @cached_property
+ def qr(self) -> QrResource:
+ """
+ First-party sign-in and encrypted messaging setup for Beeper Desktop and Beeper Server.
+ """
+ return QrResource(self._client)
+
+ @cached_property
+ def sas(self) -> SASResource:
+ """
+ First-party sign-in and encrypted messaging setup for Beeper Desktop and Beeper Server.
+ """
+ return SASResource(self._client)
+
+ @cached_property
+ def with_raw_response(self) -> VerificationsResourceWithRawResponse:
+ """
+ This property can be used as a prefix for any HTTP method call to return
+ the raw response object instead of the parsed content.
+
+ For more information, see https://www.github.com/beeper/desktop-api-python#accessing-raw-response-data-eg-headers
+ """
+ return VerificationsResourceWithRawResponse(self)
+
+ @cached_property
+ def with_streaming_response(self) -> VerificationsResourceWithStreamingResponse:
+ """
+ An alternative to `.with_raw_response` that doesn't eagerly read the response body.
+
+ For more information, see https://www.github.com/beeper/desktop-api-python#with_streaming_response
+ """
+ return VerificationsResourceWithStreamingResponse(self)
+
+ def create(
+ self,
+ *,
+ purpose: Literal["login", "device"] | Omit = omit,
+ user_id: str | Omit = omit,
+ # Use the following arguments if you need to pass additional parameters to the API that aren't available via kwargs.
+ # The extra values given here take precedence over values defined on the client or passed to this method.
+ extra_headers: Headers | None = None,
+ extra_query: Query | None = None,
+ extra_body: Body | None = None,
+ timeout: float | httpx.Timeout | None | NotGiven = not_given,
+ ) -> VerificationCreateResponse:
+ """
+ Start verifying this device from another signed-in device.
+
+ Args:
+ purpose: Why this verification is being started.
+
+ user_id: Beeper user ID to verify. Defaults to the signed-in user.
+
+ extra_headers: Send extra headers
+
+ extra_query: Add additional query parameters to the request
+
+ extra_body: Add additional JSON properties to the request
+
+ timeout: Override the client-level default timeout for this request, in seconds
+ """
+ return self._post(
+ "/v1/app/setup/verifications",
+ body=maybe_transform(
+ {
+ "purpose": purpose,
+ "user_id": user_id,
+ },
+ verification_create_params.VerificationCreateParams,
+ ),
+ options=make_request_options(
+ extra_headers=extra_headers, extra_query=extra_query, extra_body=extra_body, timeout=timeout
+ ),
+ cast_to=VerificationCreateResponse,
+ )
+
+ def retrieve(
+ self,
+ verification_id: str,
+ *,
+ # Use the following arguments if you need to pass additional parameters to the API that aren't available via kwargs.
+ # The extra values given here take precedence over values defined on the client or passed to this method.
+ extra_headers: Headers | None = None,
+ extra_query: Query | None = None,
+ extra_body: Body | None = None,
+ timeout: float | httpx.Timeout | None | NotGiven = not_given,
+ ) -> VerificationRetrieveResponse:
+ """
+ Get the current state of a device verification transaction.
+
+ Args:
+ verification_id: Verification ID.
+
+ extra_headers: Send extra headers
+
+ extra_query: Add additional query parameters to the request
+
+ extra_body: Add additional JSON properties to the request
+
+ timeout: Override the client-level default timeout for this request, in seconds
+ """
+ if not verification_id:
+ raise ValueError(f"Expected a non-empty value for `verification_id` but received {verification_id!r}")
+ return self._get(
+ path_template("/v1/app/setup/verifications/{verification_id}", verification_id=verification_id),
+ options=make_request_options(
+ extra_headers=extra_headers, extra_query=extra_query, extra_body=extra_body, timeout=timeout
+ ),
+ cast_to=VerificationRetrieveResponse,
+ )
+
+ def list(
+ self,
+ *,
+ # Use the following arguments if you need to pass additional parameters to the API that aren't available via kwargs.
+ # The extra values given here take precedence over values defined on the client or passed to this method.
+ extra_headers: Headers | None = None,
+ extra_query: Query | None = None,
+ extra_body: Body | None = None,
+ timeout: float | httpx.Timeout | None | NotGiven = not_given,
+ ) -> VerificationListResponse:
+ """List pending and active device verifications.
+
+ Use this to recover state without
+ a WebSocket connection.
+ """
+ return self._get(
+ "/v1/app/setup/verifications",
+ options=make_request_options(
+ extra_headers=extra_headers, extra_query=extra_query, extra_body=extra_body, timeout=timeout
+ ),
+ cast_to=VerificationListResponse,
+ )
+
+ def accept(
+ self,
+ verification_id: str,
+ *,
+ # Use the following arguments if you need to pass additional parameters to the API that aren't available via kwargs.
+ # The extra values given here take precedence over values defined on the client or passed to this method.
+ extra_headers: Headers | None = None,
+ extra_query: Query | None = None,
+ extra_body: Body | None = None,
+ timeout: float | httpx.Timeout | None | NotGiven = not_given,
+ ) -> VerificationAcceptResponse:
+ """
+ Accept an incoming device verification request.
+
+ Args:
+ verification_id: Verification ID.
+
+ extra_headers: Send extra headers
+
+ extra_query: Add additional query parameters to the request
+
+ extra_body: Add additional JSON properties to the request
+
+ timeout: Override the client-level default timeout for this request, in seconds
+ """
+ if not verification_id:
+ raise ValueError(f"Expected a non-empty value for `verification_id` but received {verification_id!r}")
+ return self._post(
+ path_template("/v1/app/setup/verifications/{verification_id}/accept", verification_id=verification_id),
+ options=make_request_options(
+ extra_headers=extra_headers, extra_query=extra_query, extra_body=extra_body, timeout=timeout
+ ),
+ cast_to=VerificationAcceptResponse,
+ )
+
+ def cancel(
+ self,
+ verification_id: str,
+ *,
+ code: str | Omit = omit,
+ reason: str | Omit = omit,
+ # Use the following arguments if you need to pass additional parameters to the API that aren't available via kwargs.
+ # The extra values given here take precedence over values defined on the client or passed to this method.
+ extra_headers: Headers | None = None,
+ extra_query: Query | None = None,
+ extra_body: Body | None = None,
+ timeout: float | httpx.Timeout | None | NotGiven = not_given,
+ ) -> VerificationCancelResponse:
+ """
+ Cancel an active device verification request.
+
+ Args:
+ verification_id: Verification ID.
+
+ code: Optional cancellation code.
+
+ reason: Optional user-facing cancellation reason.
+
+ extra_headers: Send extra headers
+
+ extra_query: Add additional query parameters to the request
+
+ extra_body: Add additional JSON properties to the request
+
+ timeout: Override the client-level default timeout for this request, in seconds
+ """
+ if not verification_id:
+ raise ValueError(f"Expected a non-empty value for `verification_id` but received {verification_id!r}")
+ return self._post(
+ path_template("/v1/app/setup/verifications/{verification_id}/cancel", verification_id=verification_id),
+ body=maybe_transform(
+ {
+ "code": code,
+ "reason": reason,
+ },
+ verification_cancel_params.VerificationCancelParams,
+ ),
+ options=make_request_options(
+ extra_headers=extra_headers, extra_query=extra_query, extra_body=extra_body, timeout=timeout
+ ),
+ cast_to=VerificationCancelResponse,
+ )
+
+
+class AsyncVerificationsResource(AsyncAPIResource):
+ """Manage device verification transactions"""
+
+ @cached_property
+ def qr(self) -> AsyncQrResource:
+ """
+ First-party sign-in and encrypted messaging setup for Beeper Desktop and Beeper Server.
+ """
+ return AsyncQrResource(self._client)
+
+ @cached_property
+ def sas(self) -> AsyncSASResource:
+ """
+ First-party sign-in and encrypted messaging setup for Beeper Desktop and Beeper Server.
+ """
+ return AsyncSASResource(self._client)
+
+ @cached_property
+ def with_raw_response(self) -> AsyncVerificationsResourceWithRawResponse:
+ """
+ This property can be used as a prefix for any HTTP method call to return
+ the raw response object instead of the parsed content.
+
+ For more information, see https://www.github.com/beeper/desktop-api-python#accessing-raw-response-data-eg-headers
+ """
+ return AsyncVerificationsResourceWithRawResponse(self)
+
+ @cached_property
+ def with_streaming_response(self) -> AsyncVerificationsResourceWithStreamingResponse:
+ """
+ An alternative to `.with_raw_response` that doesn't eagerly read the response body.
+
+ For more information, see https://www.github.com/beeper/desktop-api-python#with_streaming_response
+ """
+ return AsyncVerificationsResourceWithStreamingResponse(self)
+
+ async def create(
+ self,
+ *,
+ purpose: Literal["login", "device"] | Omit = omit,
+ user_id: str | Omit = omit,
+ # Use the following arguments if you need to pass additional parameters to the API that aren't available via kwargs.
+ # The extra values given here take precedence over values defined on the client or passed to this method.
+ extra_headers: Headers | None = None,
+ extra_query: Query | None = None,
+ extra_body: Body | None = None,
+ timeout: float | httpx.Timeout | None | NotGiven = not_given,
+ ) -> VerificationCreateResponse:
+ """
+ Start verifying this device from another signed-in device.
+
+ Args:
+ purpose: Why this verification is being started.
+
+ user_id: Beeper user ID to verify. Defaults to the signed-in user.
+
+ extra_headers: Send extra headers
+
+ extra_query: Add additional query parameters to the request
+
+ extra_body: Add additional JSON properties to the request
+
+ timeout: Override the client-level default timeout for this request, in seconds
+ """
+ return await self._post(
+ "/v1/app/setup/verifications",
+ body=await async_maybe_transform(
+ {
+ "purpose": purpose,
+ "user_id": user_id,
+ },
+ verification_create_params.VerificationCreateParams,
+ ),
+ options=make_request_options(
+ extra_headers=extra_headers, extra_query=extra_query, extra_body=extra_body, timeout=timeout
+ ),
+ cast_to=VerificationCreateResponse,
+ )
+
+ async def retrieve(
+ self,
+ verification_id: str,
+ *,
+ # Use the following arguments if you need to pass additional parameters to the API that aren't available via kwargs.
+ # The extra values given here take precedence over values defined on the client or passed to this method.
+ extra_headers: Headers | None = None,
+ extra_query: Query | None = None,
+ extra_body: Body | None = None,
+ timeout: float | httpx.Timeout | None | NotGiven = not_given,
+ ) -> VerificationRetrieveResponse:
+ """
+ Get the current state of a device verification transaction.
+
+ Args:
+ verification_id: Verification ID.
+
+ extra_headers: Send extra headers
+
+ extra_query: Add additional query parameters to the request
+
+ extra_body: Add additional JSON properties to the request
+
+ timeout: Override the client-level default timeout for this request, in seconds
+ """
+ if not verification_id:
+ raise ValueError(f"Expected a non-empty value for `verification_id` but received {verification_id!r}")
+ return await self._get(
+ path_template("/v1/app/setup/verifications/{verification_id}", verification_id=verification_id),
+ options=make_request_options(
+ extra_headers=extra_headers, extra_query=extra_query, extra_body=extra_body, timeout=timeout
+ ),
+ cast_to=VerificationRetrieveResponse,
+ )
+
+ async def list(
+ self,
+ *,
+ # Use the following arguments if you need to pass additional parameters to the API that aren't available via kwargs.
+ # The extra values given here take precedence over values defined on the client or passed to this method.
+ extra_headers: Headers | None = None,
+ extra_query: Query | None = None,
+ extra_body: Body | None = None,
+ timeout: float | httpx.Timeout | None | NotGiven = not_given,
+ ) -> VerificationListResponse:
+ """List pending and active device verifications.
+
+ Use this to recover state without
+ a WebSocket connection.
+ """
+ return await self._get(
+ "/v1/app/setup/verifications",
+ options=make_request_options(
+ extra_headers=extra_headers, extra_query=extra_query, extra_body=extra_body, timeout=timeout
+ ),
+ cast_to=VerificationListResponse,
+ )
+
+ async def accept(
+ self,
+ verification_id: str,
+ *,
+ # Use the following arguments if you need to pass additional parameters to the API that aren't available via kwargs.
+ # The extra values given here take precedence over values defined on the client or passed to this method.
+ extra_headers: Headers | None = None,
+ extra_query: Query | None = None,
+ extra_body: Body | None = None,
+ timeout: float | httpx.Timeout | None | NotGiven = not_given,
+ ) -> VerificationAcceptResponse:
+ """
+ Accept an incoming device verification request.
+
+ Args:
+ verification_id: Verification ID.
+
+ extra_headers: Send extra headers
+
+ extra_query: Add additional query parameters to the request
+
+ extra_body: Add additional JSON properties to the request
+
+ timeout: Override the client-level default timeout for this request, in seconds
+ """
+ if not verification_id:
+ raise ValueError(f"Expected a non-empty value for `verification_id` but received {verification_id!r}")
+ return await self._post(
+ path_template("/v1/app/setup/verifications/{verification_id}/accept", verification_id=verification_id),
+ options=make_request_options(
+ extra_headers=extra_headers, extra_query=extra_query, extra_body=extra_body, timeout=timeout
+ ),
+ cast_to=VerificationAcceptResponse,
+ )
+
+ async def cancel(
+ self,
+ verification_id: str,
+ *,
+ code: str | Omit = omit,
+ reason: str | Omit = omit,
+ # Use the following arguments if you need to pass additional parameters to the API that aren't available via kwargs.
+ # The extra values given here take precedence over values defined on the client or passed to this method.
+ extra_headers: Headers | None = None,
+ extra_query: Query | None = None,
+ extra_body: Body | None = None,
+ timeout: float | httpx.Timeout | None | NotGiven = not_given,
+ ) -> VerificationCancelResponse:
+ """
+ Cancel an active device verification request.
+
+ Args:
+ verification_id: Verification ID.
+
+ code: Optional cancellation code.
+
+ reason: Optional user-facing cancellation reason.
+
+ extra_headers: Send extra headers
+
+ extra_query: Add additional query parameters to the request
+
+ extra_body: Add additional JSON properties to the request
+
+ timeout: Override the client-level default timeout for this request, in seconds
+ """
+ if not verification_id:
+ raise ValueError(f"Expected a non-empty value for `verification_id` but received {verification_id!r}")
+ return await self._post(
+ path_template("/v1/app/setup/verifications/{verification_id}/cancel", verification_id=verification_id),
+ body=await async_maybe_transform(
+ {
+ "code": code,
+ "reason": reason,
+ },
+ verification_cancel_params.VerificationCancelParams,
+ ),
+ options=make_request_options(
+ extra_headers=extra_headers, extra_query=extra_query, extra_body=extra_body, timeout=timeout
+ ),
+ cast_to=VerificationCancelResponse,
+ )
+
+
+class VerificationsResourceWithRawResponse:
+ def __init__(self, verifications: VerificationsResource) -> None:
+ self._verifications = verifications
+
+ self.create = to_raw_response_wrapper(
+ verifications.create,
+ )
+ self.retrieve = to_raw_response_wrapper(
+ verifications.retrieve,
+ )
+ self.list = to_raw_response_wrapper(
+ verifications.list,
+ )
+ self.accept = to_raw_response_wrapper(
+ verifications.accept,
+ )
+ self.cancel = to_raw_response_wrapper(
+ verifications.cancel,
+ )
+
+ @cached_property
+ def qr(self) -> QrResourceWithRawResponse:
+ """
+ First-party sign-in and encrypted messaging setup for Beeper Desktop and Beeper Server.
+ """
+ return QrResourceWithRawResponse(self._verifications.qr)
+
+ @cached_property
+ def sas(self) -> SASResourceWithRawResponse:
+ """
+ First-party sign-in and encrypted messaging setup for Beeper Desktop and Beeper Server.
+ """
+ return SASResourceWithRawResponse(self._verifications.sas)
+
+
+class AsyncVerificationsResourceWithRawResponse:
+ def __init__(self, verifications: AsyncVerificationsResource) -> None:
+ self._verifications = verifications
+
+ self.create = async_to_raw_response_wrapper(
+ verifications.create,
+ )
+ self.retrieve = async_to_raw_response_wrapper(
+ verifications.retrieve,
+ )
+ self.list = async_to_raw_response_wrapper(
+ verifications.list,
+ )
+ self.accept = async_to_raw_response_wrapper(
+ verifications.accept,
+ )
+ self.cancel = async_to_raw_response_wrapper(
+ verifications.cancel,
+ )
+
+ @cached_property
+ def qr(self) -> AsyncQrResourceWithRawResponse:
+ """
+ First-party sign-in and encrypted messaging setup for Beeper Desktop and Beeper Server.
+ """
+ return AsyncQrResourceWithRawResponse(self._verifications.qr)
+
+ @cached_property
+ def sas(self) -> AsyncSASResourceWithRawResponse:
+ """
+ First-party sign-in and encrypted messaging setup for Beeper Desktop and Beeper Server.
+ """
+ return AsyncSASResourceWithRawResponse(self._verifications.sas)
+
+
+class VerificationsResourceWithStreamingResponse:
+ def __init__(self, verifications: VerificationsResource) -> None:
+ self._verifications = verifications
+
+ self.create = to_streamed_response_wrapper(
+ verifications.create,
+ )
+ self.retrieve = to_streamed_response_wrapper(
+ verifications.retrieve,
+ )
+ self.list = to_streamed_response_wrapper(
+ verifications.list,
+ )
+ self.accept = to_streamed_response_wrapper(
+ verifications.accept,
+ )
+ self.cancel = to_streamed_response_wrapper(
+ verifications.cancel,
+ )
+
+ @cached_property
+ def qr(self) -> QrResourceWithStreamingResponse:
+ """
+ First-party sign-in and encrypted messaging setup for Beeper Desktop and Beeper Server.
+ """
+ return QrResourceWithStreamingResponse(self._verifications.qr)
+
+ @cached_property
+ def sas(self) -> SASResourceWithStreamingResponse:
+ """
+ First-party sign-in and encrypted messaging setup for Beeper Desktop and Beeper Server.
+ """
+ return SASResourceWithStreamingResponse(self._verifications.sas)
+
+
+class AsyncVerificationsResourceWithStreamingResponse:
+ def __init__(self, verifications: AsyncVerificationsResource) -> None:
+ self._verifications = verifications
+
+ self.create = async_to_streamed_response_wrapper(
+ verifications.create,
+ )
+ self.retrieve = async_to_streamed_response_wrapper(
+ verifications.retrieve,
+ )
+ self.list = async_to_streamed_response_wrapper(
+ verifications.list,
+ )
+ self.accept = async_to_streamed_response_wrapper(
+ verifications.accept,
+ )
+ self.cancel = async_to_streamed_response_wrapper(
+ verifications.cancel,
+ )
+
+ @cached_property
+ def qr(self) -> AsyncQrResourceWithStreamingResponse:
+ """
+ First-party sign-in and encrypted messaging setup for Beeper Desktop and Beeper Server.
+ """
+ return AsyncQrResourceWithStreamingResponse(self._verifications.qr)
+
+ @cached_property
+ def sas(self) -> AsyncSASResourceWithStreamingResponse:
+ """
+ First-party sign-in and encrypted messaging setup for Beeper Desktop and Beeper Server.
+ """
+ return AsyncSASResourceWithStreamingResponse(self._verifications.sas)
diff --git a/src/beeper_desktop_api/resources/assets.py b/src/beeper_desktop_api/resources/assets.py
index ccfeab4..a67081f 100644
--- a/src/beeper_desktop_api/resources/assets.py
+++ b/src/beeper_desktop_api/resources/assets.py
@@ -68,11 +68,11 @@ def download(
timeout: float | httpx.Timeout | None | NotGiven = not_given,
) -> AssetDownloadResponse:
"""
- Download a Matrix file using its mxc:// or localmxc:// URL to the device running
- Beeper Desktop and return the local file URL.
+ Download a file from an mxc:// or localmxc:// URL to the device running the
+ Beeper Client API and return the local file URL.
Args:
- url: Matrix content URL (mxc:// or localmxc://) for the file to download.
+ url: Beeper media URL (mxc:// or localmxc://) for the file to download.
extra_headers: Send extra headers
@@ -147,7 +147,7 @@ def upload(
"""Upload a file to a temporary location using multipart/form-data.
Returns an
- uploadID that can be referenced when sending a message or materializing a draft
+ uploadID that can be referenced when sending a message or creating a draft
attachment.
Args:
@@ -204,8 +204,8 @@ def upload_base64(
"""Upload a file using a JSON body with base64-encoded content.
Returns an uploadID
- that can be referenced when sending a message or materializing a draft
- attachment. Alternative to the multipart upload endpoint.
+ that can be referenced when sending a message or creating a draft attachment.
+ Alternative to the multipart upload endpoint.
Args:
content: Base64-encoded file content (max ~500MB decoded)
@@ -273,11 +273,11 @@ async def download(
timeout: float | httpx.Timeout | None | NotGiven = not_given,
) -> AssetDownloadResponse:
"""
- Download a Matrix file using its mxc:// or localmxc:// URL to the device running
- Beeper Desktop and return the local file URL.
+ Download a file from an mxc:// or localmxc:// URL to the device running the
+ Beeper Client API and return the local file URL.
Args:
- url: Matrix content URL (mxc:// or localmxc://) for the file to download.
+ url: Beeper media URL (mxc:// or localmxc://) for the file to download.
extra_headers: Send extra headers
@@ -352,7 +352,7 @@ async def upload(
"""Upload a file to a temporary location using multipart/form-data.
Returns an
- uploadID that can be referenced when sending a message or materializing a draft
+ uploadID that can be referenced when sending a message or creating a draft
attachment.
Args:
@@ -409,8 +409,8 @@ async def upload_base64(
"""Upload a file using a JSON body with base64-encoded content.
Returns an uploadID
- that can be referenced when sending a message or materializing a draft
- attachment. Alternative to the multipart upload endpoint.
+ that can be referenced when sending a message or creating a draft attachment.
+ Alternative to the multipart upload endpoint.
Args:
content: Base64-encoded file content (max ~500MB decoded)
diff --git a/src/beeper_desktop_api/resources/bridges/__init__.py b/src/beeper_desktop_api/resources/bridges/__init__.py
new file mode 100644
index 0000000..e0e3ac7
--- /dev/null
+++ b/src/beeper_desktop_api/resources/bridges/__init__.py
@@ -0,0 +1,47 @@
+# File generated from our OpenAPI spec by Stainless. See CONTRIBUTING.md for details.
+
+from .bridges import (
+ BridgesResource,
+ AsyncBridgesResource,
+ BridgesResourceWithRawResponse,
+ AsyncBridgesResourceWithRawResponse,
+ BridgesResourceWithStreamingResponse,
+ AsyncBridgesResourceWithStreamingResponse,
+)
+from .login_flows import (
+ LoginFlowsResource,
+ AsyncLoginFlowsResource,
+ LoginFlowsResourceWithRawResponse,
+ AsyncLoginFlowsResourceWithRawResponse,
+ LoginFlowsResourceWithStreamingResponse,
+ AsyncLoginFlowsResourceWithStreamingResponse,
+)
+from .login_sessions import (
+ LoginSessionsResource,
+ AsyncLoginSessionsResource,
+ LoginSessionsResourceWithRawResponse,
+ AsyncLoginSessionsResourceWithRawResponse,
+ LoginSessionsResourceWithStreamingResponse,
+ AsyncLoginSessionsResourceWithStreamingResponse,
+)
+
+__all__ = [
+ "LoginFlowsResource",
+ "AsyncLoginFlowsResource",
+ "LoginFlowsResourceWithRawResponse",
+ "AsyncLoginFlowsResourceWithRawResponse",
+ "LoginFlowsResourceWithStreamingResponse",
+ "AsyncLoginFlowsResourceWithStreamingResponse",
+ "LoginSessionsResource",
+ "AsyncLoginSessionsResource",
+ "LoginSessionsResourceWithRawResponse",
+ "AsyncLoginSessionsResourceWithRawResponse",
+ "LoginSessionsResourceWithStreamingResponse",
+ "AsyncLoginSessionsResourceWithStreamingResponse",
+ "BridgesResource",
+ "AsyncBridgesResource",
+ "BridgesResourceWithRawResponse",
+ "AsyncBridgesResourceWithRawResponse",
+ "BridgesResourceWithStreamingResponse",
+ "AsyncBridgesResourceWithStreamingResponse",
+]
diff --git a/src/beeper_desktop_api/resources/bridges/bridges.py b/src/beeper_desktop_api/resources/bridges/bridges.py
new file mode 100644
index 0000000..c34145c
--- /dev/null
+++ b/src/beeper_desktop_api/resources/bridges/bridges.py
@@ -0,0 +1,420 @@
+# File generated from our OpenAPI spec by Stainless. See CONTRIBUTING.md for details.
+
+from __future__ import annotations
+
+import httpx
+
+from ..._types import Body, Query, Headers, NotGiven, not_given
+from ..._utils import path_template
+from ..._compat import cached_property
+from ..._resource import SyncAPIResource, AsyncAPIResource
+from ..._response import (
+ to_raw_response_wrapper,
+ to_streamed_response_wrapper,
+ async_to_raw_response_wrapper,
+ async_to_streamed_response_wrapper,
+)
+from .login_flows import (
+ LoginFlowsResource,
+ AsyncLoginFlowsResource,
+ LoginFlowsResourceWithRawResponse,
+ AsyncLoginFlowsResourceWithRawResponse,
+ LoginFlowsResourceWithStreamingResponse,
+ AsyncLoginFlowsResourceWithStreamingResponse,
+)
+from ..._base_client import make_request_options
+from ...types.bridge_list_response import BridgeListResponse
+from .login_sessions.login_sessions import (
+ LoginSessionsResource,
+ AsyncLoginSessionsResource,
+ LoginSessionsResourceWithRawResponse,
+ AsyncLoginSessionsResourceWithRawResponse,
+ LoginSessionsResourceWithStreamingResponse,
+ AsyncLoginSessionsResourceWithStreamingResponse,
+)
+from ...types.bridge_retrieve_response import BridgeRetrieveResponse
+from ...types.provisioning_capabilities import ProvisioningCapabilities
+
+__all__ = ["BridgesResource", "AsyncBridgesResource"]
+
+
+class BridgesResource(SyncAPIResource):
+ """Manage bridge-backed account types, connections, and login sessions"""
+
+ @cached_property
+ def login_flows(self) -> LoginFlowsResource:
+ """
+ Available bridges, bridge logins, login sessions for connect and reconnect flows, and advanced network capabilities.
+ """
+ return LoginFlowsResource(self._client)
+
+ @cached_property
+ def login_sessions(self) -> LoginSessionsResource:
+ """
+ Available bridges, bridge logins, login sessions for connect and reconnect flows, and advanced network capabilities.
+ """
+ return LoginSessionsResource(self._client)
+
+ @cached_property
+ def with_raw_response(self) -> BridgesResourceWithRawResponse:
+ """
+ This property can be used as a prefix for any HTTP method call to return
+ the raw response object instead of the parsed content.
+
+ For more information, see https://www.github.com/beeper/desktop-api-python#accessing-raw-response-data-eg-headers
+ """
+ return BridgesResourceWithRawResponse(self)
+
+ @cached_property
+ def with_streaming_response(self) -> BridgesResourceWithStreamingResponse:
+ """
+ An alternative to `.with_raw_response` that doesn't eagerly read the response body.
+
+ For more information, see https://www.github.com/beeper/desktop-api-python#with_streaming_response
+ """
+ return BridgesResourceWithStreamingResponse(self)
+
+ def retrieve(
+ self,
+ bridge_id: str,
+ *,
+ # Use the following arguments if you need to pass additional parameters to the API that aren't available via kwargs.
+ # The extra values given here take precedence over values defined on the client or passed to this method.
+ extra_headers: Headers | None = None,
+ extra_query: Query | None = None,
+ extra_body: Body | None = None,
+ timeout: float | httpx.Timeout | None | NotGiven = not_given,
+ ) -> BridgeRetrieveResponse:
+ """
+ Get one bridge, including the chat accounts connected through it.
+
+ Args:
+ bridge_id: Bridge ID.
+
+ extra_headers: Send extra headers
+
+ extra_query: Add additional query parameters to the request
+
+ extra_body: Add additional JSON properties to the request
+
+ timeout: Override the client-level default timeout for this request, in seconds
+ """
+ if not bridge_id:
+ raise ValueError(f"Expected a non-empty value for `bridge_id` but received {bridge_id!r}")
+ return self._get(
+ path_template("/v1/bridges/{bridge_id}", bridge_id=bridge_id),
+ options=make_request_options(
+ extra_headers=extra_headers, extra_query=extra_query, extra_body=extra_body, timeout=timeout
+ ),
+ cast_to=BridgeRetrieveResponse,
+ )
+
+ def list(
+ self,
+ *,
+ # Use the following arguments if you need to pass additional parameters to the API that aren't available via kwargs.
+ # The extra values given here take precedence over values defined on the client or passed to this method.
+ extra_headers: Headers | None = None,
+ extra_query: Query | None = None,
+ extra_body: Body | None = None,
+ timeout: float | httpx.Timeout | None | NotGiven = not_given,
+ ) -> BridgeListResponse:
+ """List available bridges.
+
+ A bridge is a chat-network connector that can connect or
+ reconnect chat accounts. Connected accounts use the same Account schema as GET
+ /v1/accounts.
+ """
+ return self._get(
+ "/v1/bridges",
+ options=make_request_options(
+ extra_headers=extra_headers, extra_query=extra_query, extra_body=extra_body, timeout=timeout
+ ),
+ cast_to=BridgeListResponse,
+ )
+
+ def retrieve_capabilities(
+ self,
+ bridge_id: str,
+ *,
+ # Use the following arguments if you need to pass additional parameters to the API that aren't available via kwargs.
+ # The extra values given here take precedence over values defined on the client or passed to this method.
+ extra_headers: Headers | None = None,
+ extra_query: Query | None = None,
+ extra_body: Body | None = None,
+ timeout: float | httpx.Timeout | None | NotGiven = not_given,
+ ) -> ProvisioningCapabilities:
+ """Get advanced network capabilities for a bridge.
+
+ This endpoint is intended for
+ clients that build custom connect or chat-creation flows.
+
+ Args:
+ bridge_id: Bridge ID.
+
+ extra_headers: Send extra headers
+
+ extra_query: Add additional query parameters to the request
+
+ extra_body: Add additional JSON properties to the request
+
+ timeout: Override the client-level default timeout for this request, in seconds
+ """
+ if not bridge_id:
+ raise ValueError(f"Expected a non-empty value for `bridge_id` but received {bridge_id!r}")
+ return self._get(
+ path_template("/v1/bridges/{bridge_id}/capabilities", bridge_id=bridge_id),
+ options=make_request_options(
+ extra_headers=extra_headers, extra_query=extra_query, extra_body=extra_body, timeout=timeout
+ ),
+ cast_to=ProvisioningCapabilities,
+ )
+
+
+class AsyncBridgesResource(AsyncAPIResource):
+ """Manage bridge-backed account types, connections, and login sessions"""
+
+ @cached_property
+ def login_flows(self) -> AsyncLoginFlowsResource:
+ """
+ Available bridges, bridge logins, login sessions for connect and reconnect flows, and advanced network capabilities.
+ """
+ return AsyncLoginFlowsResource(self._client)
+
+ @cached_property
+ def login_sessions(self) -> AsyncLoginSessionsResource:
+ """
+ Available bridges, bridge logins, login sessions for connect and reconnect flows, and advanced network capabilities.
+ """
+ return AsyncLoginSessionsResource(self._client)
+
+ @cached_property
+ def with_raw_response(self) -> AsyncBridgesResourceWithRawResponse:
+ """
+ This property can be used as a prefix for any HTTP method call to return
+ the raw response object instead of the parsed content.
+
+ For more information, see https://www.github.com/beeper/desktop-api-python#accessing-raw-response-data-eg-headers
+ """
+ return AsyncBridgesResourceWithRawResponse(self)
+
+ @cached_property
+ def with_streaming_response(self) -> AsyncBridgesResourceWithStreamingResponse:
+ """
+ An alternative to `.with_raw_response` that doesn't eagerly read the response body.
+
+ For more information, see https://www.github.com/beeper/desktop-api-python#with_streaming_response
+ """
+ return AsyncBridgesResourceWithStreamingResponse(self)
+
+ async def retrieve(
+ self,
+ bridge_id: str,
+ *,
+ # Use the following arguments if you need to pass additional parameters to the API that aren't available via kwargs.
+ # The extra values given here take precedence over values defined on the client or passed to this method.
+ extra_headers: Headers | None = None,
+ extra_query: Query | None = None,
+ extra_body: Body | None = None,
+ timeout: float | httpx.Timeout | None | NotGiven = not_given,
+ ) -> BridgeRetrieveResponse:
+ """
+ Get one bridge, including the chat accounts connected through it.
+
+ Args:
+ bridge_id: Bridge ID.
+
+ extra_headers: Send extra headers
+
+ extra_query: Add additional query parameters to the request
+
+ extra_body: Add additional JSON properties to the request
+
+ timeout: Override the client-level default timeout for this request, in seconds
+ """
+ if not bridge_id:
+ raise ValueError(f"Expected a non-empty value for `bridge_id` but received {bridge_id!r}")
+ return await self._get(
+ path_template("/v1/bridges/{bridge_id}", bridge_id=bridge_id),
+ options=make_request_options(
+ extra_headers=extra_headers, extra_query=extra_query, extra_body=extra_body, timeout=timeout
+ ),
+ cast_to=BridgeRetrieveResponse,
+ )
+
+ async def list(
+ self,
+ *,
+ # Use the following arguments if you need to pass additional parameters to the API that aren't available via kwargs.
+ # The extra values given here take precedence over values defined on the client or passed to this method.
+ extra_headers: Headers | None = None,
+ extra_query: Query | None = None,
+ extra_body: Body | None = None,
+ timeout: float | httpx.Timeout | None | NotGiven = not_given,
+ ) -> BridgeListResponse:
+ """List available bridges.
+
+ A bridge is a chat-network connector that can connect or
+ reconnect chat accounts. Connected accounts use the same Account schema as GET
+ /v1/accounts.
+ """
+ return await self._get(
+ "/v1/bridges",
+ options=make_request_options(
+ extra_headers=extra_headers, extra_query=extra_query, extra_body=extra_body, timeout=timeout
+ ),
+ cast_to=BridgeListResponse,
+ )
+
+ async def retrieve_capabilities(
+ self,
+ bridge_id: str,
+ *,
+ # Use the following arguments if you need to pass additional parameters to the API that aren't available via kwargs.
+ # The extra values given here take precedence over values defined on the client or passed to this method.
+ extra_headers: Headers | None = None,
+ extra_query: Query | None = None,
+ extra_body: Body | None = None,
+ timeout: float | httpx.Timeout | None | NotGiven = not_given,
+ ) -> ProvisioningCapabilities:
+ """Get advanced network capabilities for a bridge.
+
+ This endpoint is intended for
+ clients that build custom connect or chat-creation flows.
+
+ Args:
+ bridge_id: Bridge ID.
+
+ extra_headers: Send extra headers
+
+ extra_query: Add additional query parameters to the request
+
+ extra_body: Add additional JSON properties to the request
+
+ timeout: Override the client-level default timeout for this request, in seconds
+ """
+ if not bridge_id:
+ raise ValueError(f"Expected a non-empty value for `bridge_id` but received {bridge_id!r}")
+ return await self._get(
+ path_template("/v1/bridges/{bridge_id}/capabilities", bridge_id=bridge_id),
+ options=make_request_options(
+ extra_headers=extra_headers, extra_query=extra_query, extra_body=extra_body, timeout=timeout
+ ),
+ cast_to=ProvisioningCapabilities,
+ )
+
+
+class BridgesResourceWithRawResponse:
+ def __init__(self, bridges: BridgesResource) -> None:
+ self._bridges = bridges
+
+ self.retrieve = to_raw_response_wrapper(
+ bridges.retrieve,
+ )
+ self.list = to_raw_response_wrapper(
+ bridges.list,
+ )
+ self.retrieve_capabilities = to_raw_response_wrapper(
+ bridges.retrieve_capabilities,
+ )
+
+ @cached_property
+ def login_flows(self) -> LoginFlowsResourceWithRawResponse:
+ """
+ Available bridges, bridge logins, login sessions for connect and reconnect flows, and advanced network capabilities.
+ """
+ return LoginFlowsResourceWithRawResponse(self._bridges.login_flows)
+
+ @cached_property
+ def login_sessions(self) -> LoginSessionsResourceWithRawResponse:
+ """
+ Available bridges, bridge logins, login sessions for connect and reconnect flows, and advanced network capabilities.
+ """
+ return LoginSessionsResourceWithRawResponse(self._bridges.login_sessions)
+
+
+class AsyncBridgesResourceWithRawResponse:
+ def __init__(self, bridges: AsyncBridgesResource) -> None:
+ self._bridges = bridges
+
+ self.retrieve = async_to_raw_response_wrapper(
+ bridges.retrieve,
+ )
+ self.list = async_to_raw_response_wrapper(
+ bridges.list,
+ )
+ self.retrieve_capabilities = async_to_raw_response_wrapper(
+ bridges.retrieve_capabilities,
+ )
+
+ @cached_property
+ def login_flows(self) -> AsyncLoginFlowsResourceWithRawResponse:
+ """
+ Available bridges, bridge logins, login sessions for connect and reconnect flows, and advanced network capabilities.
+ """
+ return AsyncLoginFlowsResourceWithRawResponse(self._bridges.login_flows)
+
+ @cached_property
+ def login_sessions(self) -> AsyncLoginSessionsResourceWithRawResponse:
+ """
+ Available bridges, bridge logins, login sessions for connect and reconnect flows, and advanced network capabilities.
+ """
+ return AsyncLoginSessionsResourceWithRawResponse(self._bridges.login_sessions)
+
+
+class BridgesResourceWithStreamingResponse:
+ def __init__(self, bridges: BridgesResource) -> None:
+ self._bridges = bridges
+
+ self.retrieve = to_streamed_response_wrapper(
+ bridges.retrieve,
+ )
+ self.list = to_streamed_response_wrapper(
+ bridges.list,
+ )
+ self.retrieve_capabilities = to_streamed_response_wrapper(
+ bridges.retrieve_capabilities,
+ )
+
+ @cached_property
+ def login_flows(self) -> LoginFlowsResourceWithStreamingResponse:
+ """
+ Available bridges, bridge logins, login sessions for connect and reconnect flows, and advanced network capabilities.
+ """
+ return LoginFlowsResourceWithStreamingResponse(self._bridges.login_flows)
+
+ @cached_property
+ def login_sessions(self) -> LoginSessionsResourceWithStreamingResponse:
+ """
+ Available bridges, bridge logins, login sessions for connect and reconnect flows, and advanced network capabilities.
+ """
+ return LoginSessionsResourceWithStreamingResponse(self._bridges.login_sessions)
+
+
+class AsyncBridgesResourceWithStreamingResponse:
+ def __init__(self, bridges: AsyncBridgesResource) -> None:
+ self._bridges = bridges
+
+ self.retrieve = async_to_streamed_response_wrapper(
+ bridges.retrieve,
+ )
+ self.list = async_to_streamed_response_wrapper(
+ bridges.list,
+ )
+ self.retrieve_capabilities = async_to_streamed_response_wrapper(
+ bridges.retrieve_capabilities,
+ )
+
+ @cached_property
+ def login_flows(self) -> AsyncLoginFlowsResourceWithStreamingResponse:
+ """
+ Available bridges, bridge logins, login sessions for connect and reconnect flows, and advanced network capabilities.
+ """
+ return AsyncLoginFlowsResourceWithStreamingResponse(self._bridges.login_flows)
+
+ @cached_property
+ def login_sessions(self) -> AsyncLoginSessionsResourceWithStreamingResponse:
+ """
+ Available bridges, bridge logins, login sessions for connect and reconnect flows, and advanced network capabilities.
+ """
+ return AsyncLoginSessionsResourceWithStreamingResponse(self._bridges.login_sessions)
diff --git a/src/beeper_desktop_api/resources/bridges/login_flows.py b/src/beeper_desktop_api/resources/bridges/login_flows.py
new file mode 100644
index 0000000..bc14e73
--- /dev/null
+++ b/src/beeper_desktop_api/resources/bridges/login_flows.py
@@ -0,0 +1,180 @@
+# File generated from our OpenAPI spec by Stainless. See CONTRIBUTING.md for details.
+
+from __future__ import annotations
+
+import httpx
+
+from ..._types import Body, Query, Headers, NotGiven, not_given
+from ..._utils import path_template
+from ..._compat import cached_property
+from ..._resource import SyncAPIResource, AsyncAPIResource
+from ..._response import (
+ to_raw_response_wrapper,
+ to_streamed_response_wrapper,
+ async_to_raw_response_wrapper,
+ async_to_streamed_response_wrapper,
+)
+from ..._base_client import make_request_options
+from ...types.bridges.login_flow_list_response import LoginFlowListResponse
+
+__all__ = ["LoginFlowsResource", "AsyncLoginFlowsResource"]
+
+
+class LoginFlowsResource(SyncAPIResource):
+ """
+ Available bridges, bridge logins, login sessions for connect and reconnect flows, and advanced network capabilities.
+ """
+
+ @cached_property
+ def with_raw_response(self) -> LoginFlowsResourceWithRawResponse:
+ """
+ This property can be used as a prefix for any HTTP method call to return
+ the raw response object instead of the parsed content.
+
+ For more information, see https://www.github.com/beeper/desktop-api-python#accessing-raw-response-data-eg-headers
+ """
+ return LoginFlowsResourceWithRawResponse(self)
+
+ @cached_property
+ def with_streaming_response(self) -> LoginFlowsResourceWithStreamingResponse:
+ """
+ An alternative to `.with_raw_response` that doesn't eagerly read the response body.
+
+ For more information, see https://www.github.com/beeper/desktop-api-python#with_streaming_response
+ """
+ return LoginFlowsResourceWithStreamingResponse(self)
+
+ def list(
+ self,
+ bridge_id: str,
+ *,
+ # Use the following arguments if you need to pass additional parameters to the API that aren't available via kwargs.
+ # The extra values given here take precedence over values defined on the client or passed to this method.
+ extra_headers: Headers | None = None,
+ extra_query: Query | None = None,
+ extra_body: Body | None = None,
+ timeout: float | httpx.Timeout | None | NotGiven = not_given,
+ ) -> LoginFlowListResponse:
+ """List connect and reconnect flow options for a bridge.
+
+ Use a flowID when creating
+ a bridge login session.
+
+ Args:
+ bridge_id: Bridge ID.
+
+ extra_headers: Send extra headers
+
+ extra_query: Add additional query parameters to the request
+
+ extra_body: Add additional JSON properties to the request
+
+ timeout: Override the client-level default timeout for this request, in seconds
+ """
+ if not bridge_id:
+ raise ValueError(f"Expected a non-empty value for `bridge_id` but received {bridge_id!r}")
+ return self._get(
+ path_template("/v1/bridges/{bridge_id}/login-flows", bridge_id=bridge_id),
+ options=make_request_options(
+ extra_headers=extra_headers, extra_query=extra_query, extra_body=extra_body, timeout=timeout
+ ),
+ cast_to=LoginFlowListResponse,
+ )
+
+
+class AsyncLoginFlowsResource(AsyncAPIResource):
+ """
+ Available bridges, bridge logins, login sessions for connect and reconnect flows, and advanced network capabilities.
+ """
+
+ @cached_property
+ def with_raw_response(self) -> AsyncLoginFlowsResourceWithRawResponse:
+ """
+ This property can be used as a prefix for any HTTP method call to return
+ the raw response object instead of the parsed content.
+
+ For more information, see https://www.github.com/beeper/desktop-api-python#accessing-raw-response-data-eg-headers
+ """
+ return AsyncLoginFlowsResourceWithRawResponse(self)
+
+ @cached_property
+ def with_streaming_response(self) -> AsyncLoginFlowsResourceWithStreamingResponse:
+ """
+ An alternative to `.with_raw_response` that doesn't eagerly read the response body.
+
+ For more information, see https://www.github.com/beeper/desktop-api-python#with_streaming_response
+ """
+ return AsyncLoginFlowsResourceWithStreamingResponse(self)
+
+ async def list(
+ self,
+ bridge_id: str,
+ *,
+ # Use the following arguments if you need to pass additional parameters to the API that aren't available via kwargs.
+ # The extra values given here take precedence over values defined on the client or passed to this method.
+ extra_headers: Headers | None = None,
+ extra_query: Query | None = None,
+ extra_body: Body | None = None,
+ timeout: float | httpx.Timeout | None | NotGiven = not_given,
+ ) -> LoginFlowListResponse:
+ """List connect and reconnect flow options for a bridge.
+
+ Use a flowID when creating
+ a bridge login session.
+
+ Args:
+ bridge_id: Bridge ID.
+
+ extra_headers: Send extra headers
+
+ extra_query: Add additional query parameters to the request
+
+ extra_body: Add additional JSON properties to the request
+
+ timeout: Override the client-level default timeout for this request, in seconds
+ """
+ if not bridge_id:
+ raise ValueError(f"Expected a non-empty value for `bridge_id` but received {bridge_id!r}")
+ return await self._get(
+ path_template("/v1/bridges/{bridge_id}/login-flows", bridge_id=bridge_id),
+ options=make_request_options(
+ extra_headers=extra_headers, extra_query=extra_query, extra_body=extra_body, timeout=timeout
+ ),
+ cast_to=LoginFlowListResponse,
+ )
+
+
+class LoginFlowsResourceWithRawResponse:
+ def __init__(self, login_flows: LoginFlowsResource) -> None:
+ self._login_flows = login_flows
+
+ self.list = to_raw_response_wrapper(
+ login_flows.list,
+ )
+
+
+class AsyncLoginFlowsResourceWithRawResponse:
+ def __init__(self, login_flows: AsyncLoginFlowsResource) -> None:
+ self._login_flows = login_flows
+
+ self.list = async_to_raw_response_wrapper(
+ login_flows.list,
+ )
+
+
+class LoginFlowsResourceWithStreamingResponse:
+ def __init__(self, login_flows: LoginFlowsResource) -> None:
+ self._login_flows = login_flows
+
+ self.list = to_streamed_response_wrapper(
+ login_flows.list,
+ )
+
+
+class AsyncLoginFlowsResourceWithStreamingResponse:
+ def __init__(self, login_flows: AsyncLoginFlowsResource) -> None:
+ self._login_flows = login_flows
+
+ self.list = async_to_streamed_response_wrapper(
+ login_flows.list,
+ )
diff --git a/src/beeper_desktop_api/resources/bridges/login_sessions/__init__.py b/src/beeper_desktop_api/resources/bridges/login_sessions/__init__.py
new file mode 100644
index 0000000..18becaa
--- /dev/null
+++ b/src/beeper_desktop_api/resources/bridges/login_sessions/__init__.py
@@ -0,0 +1,33 @@
+# File generated from our OpenAPI spec by Stainless. See CONTRIBUTING.md for details.
+
+from .steps import (
+ StepsResource,
+ AsyncStepsResource,
+ StepsResourceWithRawResponse,
+ AsyncStepsResourceWithRawResponse,
+ StepsResourceWithStreamingResponse,
+ AsyncStepsResourceWithStreamingResponse,
+)
+from .login_sessions import (
+ LoginSessionsResource,
+ AsyncLoginSessionsResource,
+ LoginSessionsResourceWithRawResponse,
+ AsyncLoginSessionsResourceWithRawResponse,
+ LoginSessionsResourceWithStreamingResponse,
+ AsyncLoginSessionsResourceWithStreamingResponse,
+)
+
+__all__ = [
+ "StepsResource",
+ "AsyncStepsResource",
+ "StepsResourceWithRawResponse",
+ "AsyncStepsResourceWithRawResponse",
+ "StepsResourceWithStreamingResponse",
+ "AsyncStepsResourceWithStreamingResponse",
+ "LoginSessionsResource",
+ "AsyncLoginSessionsResource",
+ "LoginSessionsResourceWithRawResponse",
+ "AsyncLoginSessionsResourceWithRawResponse",
+ "LoginSessionsResourceWithStreamingResponse",
+ "AsyncLoginSessionsResourceWithStreamingResponse",
+]
diff --git a/src/beeper_desktop_api/resources/bridges/login_sessions/login_sessions.py b/src/beeper_desktop_api/resources/bridges/login_sessions/login_sessions.py
new file mode 100644
index 0000000..5fd6051
--- /dev/null
+++ b/src/beeper_desktop_api/resources/bridges/login_sessions/login_sessions.py
@@ -0,0 +1,468 @@
+# File generated from our OpenAPI spec by Stainless. See CONTRIBUTING.md for details.
+
+from __future__ import annotations
+
+import httpx
+
+from .steps import (
+ StepsResource,
+ AsyncStepsResource,
+ StepsResourceWithRawResponse,
+ AsyncStepsResourceWithRawResponse,
+ StepsResourceWithStreamingResponse,
+ AsyncStepsResourceWithStreamingResponse,
+)
+from ...._types import Body, Omit, Query, Headers, NotGiven, omit, not_given
+from ...._utils import path_template, maybe_transform, async_maybe_transform
+from ...._compat import cached_property
+from ...._resource import SyncAPIResource, AsyncAPIResource
+from ...._response import (
+ to_raw_response_wrapper,
+ to_streamed_response_wrapper,
+ async_to_raw_response_wrapper,
+ async_to_streamed_response_wrapper,
+)
+from ...._base_client import make_request_options
+from ....types.bridges import login_session_create_params
+from ....types.login_session import LoginSession
+from ....types.bridges.login_session_cancel_response import LoginSessionCancelResponse
+
+__all__ = ["LoginSessionsResource", "AsyncLoginSessionsResource"]
+
+
+class LoginSessionsResource(SyncAPIResource):
+ """
+ Available bridges, bridge logins, login sessions for connect and reconnect flows, and advanced network capabilities.
+ """
+
+ @cached_property
+ def steps(self) -> StepsResource:
+ """
+ Available bridges, bridge logins, login sessions for connect and reconnect flows, and advanced network capabilities.
+ """
+ return StepsResource(self._client)
+
+ @cached_property
+ def with_raw_response(self) -> LoginSessionsResourceWithRawResponse:
+ """
+ This property can be used as a prefix for any HTTP method call to return
+ the raw response object instead of the parsed content.
+
+ For more information, see https://www.github.com/beeper/desktop-api-python#accessing-raw-response-data-eg-headers
+ """
+ return LoginSessionsResourceWithRawResponse(self)
+
+ @cached_property
+ def with_streaming_response(self) -> LoginSessionsResourceWithStreamingResponse:
+ """
+ An alternative to `.with_raw_response` that doesn't eagerly read the response body.
+
+ For more information, see https://www.github.com/beeper/desktop-api-python#with_streaming_response
+ """
+ return LoginSessionsResourceWithStreamingResponse(self)
+
+ def create(
+ self,
+ bridge_id: str,
+ *,
+ account_id: str | Omit = omit,
+ flow_id: str | Omit = omit,
+ login_id: str | Omit = omit,
+ # Use the following arguments if you need to pass additional parameters to the API that aren't available via kwargs.
+ # The extra values given here take precedence over values defined on the client or passed to this method.
+ extra_headers: Headers | None = None,
+ extra_query: Query | None = None,
+ extra_body: Body | None = None,
+ timeout: float | httpx.Timeout | None | NotGiven = not_given,
+ ) -> LoginSession:
+ """
+ Start a temporary bridge login session to connect a new chat account or
+ reconnect an existing bridge login. Omit loginID and accountID to connect a new
+ account.
+
+ Args:
+ bridge_id: Bridge ID.
+
+ account_id: Existing chat account ID to reconnect. Omit to connect a new account.
+
+ flow_id: Optional flow ID returned by the list login flows endpoint. If omitted, Beeper
+ chooses the default flow.
+
+ login_id: Existing bridge login ID to reconnect. Omit to connect a new account.
+
+ extra_headers: Send extra headers
+
+ extra_query: Add additional query parameters to the request
+
+ extra_body: Add additional JSON properties to the request
+
+ timeout: Override the client-level default timeout for this request, in seconds
+ """
+ if not bridge_id:
+ raise ValueError(f"Expected a non-empty value for `bridge_id` but received {bridge_id!r}")
+ return self._post(
+ path_template("/v1/bridges/{bridge_id}/login-sessions", bridge_id=bridge_id),
+ body=maybe_transform(
+ {
+ "account_id": account_id,
+ "flow_id": flow_id,
+ "login_id": login_id,
+ },
+ login_session_create_params.LoginSessionCreateParams,
+ ),
+ options=make_request_options(
+ extra_headers=extra_headers, extra_query=extra_query, extra_body=extra_body, timeout=timeout
+ ),
+ cast_to=LoginSession,
+ )
+
+ def retrieve(
+ self,
+ login_session_id: str,
+ *,
+ bridge_id: str,
+ # Use the following arguments if you need to pass additional parameters to the API that aren't available via kwargs.
+ # The extra values given here take precedence over values defined on the client or passed to this method.
+ extra_headers: Headers | None = None,
+ extra_query: Query | None = None,
+ extra_body: Body | None = None,
+ timeout: float | httpx.Timeout | None | NotGiven = not_given,
+ ) -> LoginSession:
+ """
+ Get the current state of a temporary bridge login session.
+
+ Args:
+ bridge_id: Bridge ID.
+
+ login_session_id: Temporary bridge login session ID.
+
+ extra_headers: Send extra headers
+
+ extra_query: Add additional query parameters to the request
+
+ extra_body: Add additional JSON properties to the request
+
+ timeout: Override the client-level default timeout for this request, in seconds
+ """
+ if not bridge_id:
+ raise ValueError(f"Expected a non-empty value for `bridge_id` but received {bridge_id!r}")
+ if not login_session_id:
+ raise ValueError(f"Expected a non-empty value for `login_session_id` but received {login_session_id!r}")
+ return self._get(
+ path_template(
+ "/v1/bridges/{bridge_id}/login-sessions/{login_session_id}",
+ bridge_id=bridge_id,
+ login_session_id=login_session_id,
+ ),
+ options=make_request_options(
+ extra_headers=extra_headers, extra_query=extra_query, extra_body=extra_body, timeout=timeout
+ ),
+ cast_to=LoginSession,
+ )
+
+ def cancel(
+ self,
+ login_session_id: str,
+ *,
+ bridge_id: str,
+ # Use the following arguments if you need to pass additional parameters to the API that aren't available via kwargs.
+ # The extra values given here take precedence over values defined on the client or passed to this method.
+ extra_headers: Headers | None = None,
+ extra_query: Query | None = None,
+ extra_body: Body | None = None,
+ timeout: float | httpx.Timeout | None | NotGiven = not_given,
+ ) -> LoginSessionCancelResponse:
+ """
+ Cancel a temporary bridge login session.
+
+ Args:
+ bridge_id: Bridge ID.
+
+ login_session_id: Temporary bridge login session ID.
+
+ extra_headers: Send extra headers
+
+ extra_query: Add additional query parameters to the request
+
+ extra_body: Add additional JSON properties to the request
+
+ timeout: Override the client-level default timeout for this request, in seconds
+ """
+ if not bridge_id:
+ raise ValueError(f"Expected a non-empty value for `bridge_id` but received {bridge_id!r}")
+ if not login_session_id:
+ raise ValueError(f"Expected a non-empty value for `login_session_id` but received {login_session_id!r}")
+ return self._delete(
+ path_template(
+ "/v1/bridges/{bridge_id}/login-sessions/{login_session_id}",
+ bridge_id=bridge_id,
+ login_session_id=login_session_id,
+ ),
+ options=make_request_options(
+ extra_headers=extra_headers, extra_query=extra_query, extra_body=extra_body, timeout=timeout
+ ),
+ cast_to=LoginSessionCancelResponse,
+ )
+
+
+class AsyncLoginSessionsResource(AsyncAPIResource):
+ """
+ Available bridges, bridge logins, login sessions for connect and reconnect flows, and advanced network capabilities.
+ """
+
+ @cached_property
+ def steps(self) -> AsyncStepsResource:
+ """
+ Available bridges, bridge logins, login sessions for connect and reconnect flows, and advanced network capabilities.
+ """
+ return AsyncStepsResource(self._client)
+
+ @cached_property
+ def with_raw_response(self) -> AsyncLoginSessionsResourceWithRawResponse:
+ """
+ This property can be used as a prefix for any HTTP method call to return
+ the raw response object instead of the parsed content.
+
+ For more information, see https://www.github.com/beeper/desktop-api-python#accessing-raw-response-data-eg-headers
+ """
+ return AsyncLoginSessionsResourceWithRawResponse(self)
+
+ @cached_property
+ def with_streaming_response(self) -> AsyncLoginSessionsResourceWithStreamingResponse:
+ """
+ An alternative to `.with_raw_response` that doesn't eagerly read the response body.
+
+ For more information, see https://www.github.com/beeper/desktop-api-python#with_streaming_response
+ """
+ return AsyncLoginSessionsResourceWithStreamingResponse(self)
+
+ async def create(
+ self,
+ bridge_id: str,
+ *,
+ account_id: str | Omit = omit,
+ flow_id: str | Omit = omit,
+ login_id: str | Omit = omit,
+ # Use the following arguments if you need to pass additional parameters to the API that aren't available via kwargs.
+ # The extra values given here take precedence over values defined on the client or passed to this method.
+ extra_headers: Headers | None = None,
+ extra_query: Query | None = None,
+ extra_body: Body | None = None,
+ timeout: float | httpx.Timeout | None | NotGiven = not_given,
+ ) -> LoginSession:
+ """
+ Start a temporary bridge login session to connect a new chat account or
+ reconnect an existing bridge login. Omit loginID and accountID to connect a new
+ account.
+
+ Args:
+ bridge_id: Bridge ID.
+
+ account_id: Existing chat account ID to reconnect. Omit to connect a new account.
+
+ flow_id: Optional flow ID returned by the list login flows endpoint. If omitted, Beeper
+ chooses the default flow.
+
+ login_id: Existing bridge login ID to reconnect. Omit to connect a new account.
+
+ extra_headers: Send extra headers
+
+ extra_query: Add additional query parameters to the request
+
+ extra_body: Add additional JSON properties to the request
+
+ timeout: Override the client-level default timeout for this request, in seconds
+ """
+ if not bridge_id:
+ raise ValueError(f"Expected a non-empty value for `bridge_id` but received {bridge_id!r}")
+ return await self._post(
+ path_template("/v1/bridges/{bridge_id}/login-sessions", bridge_id=bridge_id),
+ body=await async_maybe_transform(
+ {
+ "account_id": account_id,
+ "flow_id": flow_id,
+ "login_id": login_id,
+ },
+ login_session_create_params.LoginSessionCreateParams,
+ ),
+ options=make_request_options(
+ extra_headers=extra_headers, extra_query=extra_query, extra_body=extra_body, timeout=timeout
+ ),
+ cast_to=LoginSession,
+ )
+
+ async def retrieve(
+ self,
+ login_session_id: str,
+ *,
+ bridge_id: str,
+ # Use the following arguments if you need to pass additional parameters to the API that aren't available via kwargs.
+ # The extra values given here take precedence over values defined on the client or passed to this method.
+ extra_headers: Headers | None = None,
+ extra_query: Query | None = None,
+ extra_body: Body | None = None,
+ timeout: float | httpx.Timeout | None | NotGiven = not_given,
+ ) -> LoginSession:
+ """
+ Get the current state of a temporary bridge login session.
+
+ Args:
+ bridge_id: Bridge ID.
+
+ login_session_id: Temporary bridge login session ID.
+
+ extra_headers: Send extra headers
+
+ extra_query: Add additional query parameters to the request
+
+ extra_body: Add additional JSON properties to the request
+
+ timeout: Override the client-level default timeout for this request, in seconds
+ """
+ if not bridge_id:
+ raise ValueError(f"Expected a non-empty value for `bridge_id` but received {bridge_id!r}")
+ if not login_session_id:
+ raise ValueError(f"Expected a non-empty value for `login_session_id` but received {login_session_id!r}")
+ return await self._get(
+ path_template(
+ "/v1/bridges/{bridge_id}/login-sessions/{login_session_id}",
+ bridge_id=bridge_id,
+ login_session_id=login_session_id,
+ ),
+ options=make_request_options(
+ extra_headers=extra_headers, extra_query=extra_query, extra_body=extra_body, timeout=timeout
+ ),
+ cast_to=LoginSession,
+ )
+
+ async def cancel(
+ self,
+ login_session_id: str,
+ *,
+ bridge_id: str,
+ # Use the following arguments if you need to pass additional parameters to the API that aren't available via kwargs.
+ # The extra values given here take precedence over values defined on the client or passed to this method.
+ extra_headers: Headers | None = None,
+ extra_query: Query | None = None,
+ extra_body: Body | None = None,
+ timeout: float | httpx.Timeout | None | NotGiven = not_given,
+ ) -> LoginSessionCancelResponse:
+ """
+ Cancel a temporary bridge login session.
+
+ Args:
+ bridge_id: Bridge ID.
+
+ login_session_id: Temporary bridge login session ID.
+
+ extra_headers: Send extra headers
+
+ extra_query: Add additional query parameters to the request
+
+ extra_body: Add additional JSON properties to the request
+
+ timeout: Override the client-level default timeout for this request, in seconds
+ """
+ if not bridge_id:
+ raise ValueError(f"Expected a non-empty value for `bridge_id` but received {bridge_id!r}")
+ if not login_session_id:
+ raise ValueError(f"Expected a non-empty value for `login_session_id` but received {login_session_id!r}")
+ return await self._delete(
+ path_template(
+ "/v1/bridges/{bridge_id}/login-sessions/{login_session_id}",
+ bridge_id=bridge_id,
+ login_session_id=login_session_id,
+ ),
+ options=make_request_options(
+ extra_headers=extra_headers, extra_query=extra_query, extra_body=extra_body, timeout=timeout
+ ),
+ cast_to=LoginSessionCancelResponse,
+ )
+
+
+class LoginSessionsResourceWithRawResponse:
+ def __init__(self, login_sessions: LoginSessionsResource) -> None:
+ self._login_sessions = login_sessions
+
+ self.create = to_raw_response_wrapper(
+ login_sessions.create,
+ )
+ self.retrieve = to_raw_response_wrapper(
+ login_sessions.retrieve,
+ )
+ self.cancel = to_raw_response_wrapper(
+ login_sessions.cancel,
+ )
+
+ @cached_property
+ def steps(self) -> StepsResourceWithRawResponse:
+ """
+ Available bridges, bridge logins, login sessions for connect and reconnect flows, and advanced network capabilities.
+ """
+ return StepsResourceWithRawResponse(self._login_sessions.steps)
+
+
+class AsyncLoginSessionsResourceWithRawResponse:
+ def __init__(self, login_sessions: AsyncLoginSessionsResource) -> None:
+ self._login_sessions = login_sessions
+
+ self.create = async_to_raw_response_wrapper(
+ login_sessions.create,
+ )
+ self.retrieve = async_to_raw_response_wrapper(
+ login_sessions.retrieve,
+ )
+ self.cancel = async_to_raw_response_wrapper(
+ login_sessions.cancel,
+ )
+
+ @cached_property
+ def steps(self) -> AsyncStepsResourceWithRawResponse:
+ """
+ Available bridges, bridge logins, login sessions for connect and reconnect flows, and advanced network capabilities.
+ """
+ return AsyncStepsResourceWithRawResponse(self._login_sessions.steps)
+
+
+class LoginSessionsResourceWithStreamingResponse:
+ def __init__(self, login_sessions: LoginSessionsResource) -> None:
+ self._login_sessions = login_sessions
+
+ self.create = to_streamed_response_wrapper(
+ login_sessions.create,
+ )
+ self.retrieve = to_streamed_response_wrapper(
+ login_sessions.retrieve,
+ )
+ self.cancel = to_streamed_response_wrapper(
+ login_sessions.cancel,
+ )
+
+ @cached_property
+ def steps(self) -> StepsResourceWithStreamingResponse:
+ """
+ Available bridges, bridge logins, login sessions for connect and reconnect flows, and advanced network capabilities.
+ """
+ return StepsResourceWithStreamingResponse(self._login_sessions.steps)
+
+
+class AsyncLoginSessionsResourceWithStreamingResponse:
+ def __init__(self, login_sessions: AsyncLoginSessionsResource) -> None:
+ self._login_sessions = login_sessions
+
+ self.create = async_to_streamed_response_wrapper(
+ login_sessions.create,
+ )
+ self.retrieve = async_to_streamed_response_wrapper(
+ login_sessions.retrieve,
+ )
+ self.cancel = async_to_streamed_response_wrapper(
+ login_sessions.cancel,
+ )
+
+ @cached_property
+ def steps(self) -> AsyncStepsResourceWithStreamingResponse:
+ """
+ Available bridges, bridge logins, login sessions for connect and reconnect flows, and advanced network capabilities.
+ """
+ return AsyncStepsResourceWithStreamingResponse(self._login_sessions.steps)
diff --git a/src/beeper_desktop_api/resources/bridges/login_sessions/steps.py b/src/beeper_desktop_api/resources/bridges/login_sessions/steps.py
new file mode 100644
index 0000000..6bc590e
--- /dev/null
+++ b/src/beeper_desktop_api/resources/bridges/login_sessions/steps.py
@@ -0,0 +1,250 @@
+# File generated from our OpenAPI spec by Stainless. See CONTRIBUTING.md for details.
+
+from __future__ import annotations
+
+from typing import Dict
+from typing_extensions import Literal
+
+import httpx
+
+from ...._types import Body, Omit, Query, Headers, NotGiven, omit, not_given
+from ...._utils import path_template, maybe_transform, async_maybe_transform
+from ...._compat import cached_property
+from ...._resource import SyncAPIResource, AsyncAPIResource
+from ...._response import (
+ to_raw_response_wrapper,
+ to_streamed_response_wrapper,
+ async_to_raw_response_wrapper,
+ async_to_streamed_response_wrapper,
+)
+from ...._base_client import make_request_options
+from ....types.login_session import LoginSession
+from ....types.bridges.login_sessions import step_submit_params
+
+__all__ = ["StepsResource", "AsyncStepsResource"]
+
+
+class StepsResource(SyncAPIResource):
+ """
+ Available bridges, bridge logins, login sessions for connect and reconnect flows, and advanced network capabilities.
+ """
+
+ @cached_property
+ def with_raw_response(self) -> StepsResourceWithRawResponse:
+ """
+ This property can be used as a prefix for any HTTP method call to return
+ the raw response object instead of the parsed content.
+
+ For more information, see https://www.github.com/beeper/desktop-api-python#accessing-raw-response-data-eg-headers
+ """
+ return StepsResourceWithRawResponse(self)
+
+ @cached_property
+ def with_streaming_response(self) -> StepsResourceWithStreamingResponse:
+ """
+ An alternative to `.with_raw_response` that doesn't eagerly read the response body.
+
+ For more information, see https://www.github.com/beeper/desktop-api-python#with_streaming_response
+ """
+ return StepsResourceWithStreamingResponse(self)
+
+ def submit(
+ self,
+ step_id: str,
+ *,
+ bridge_id: str,
+ login_session_id: str,
+ type: Literal["user_input", "cookies", "display_and_wait"],
+ fields: Dict[str, str] | Omit = omit,
+ last_url: str | Omit = omit,
+ source: Literal["api", "webview", "browser_extension"] | Omit = omit,
+ # Use the following arguments if you need to pass additional parameters to the API that aren't available via kwargs.
+ # The extra values given here take precedence over values defined on the client or passed to this method.
+ extra_headers: Headers | None = None,
+ extra_query: Query | None = None,
+ extra_body: Body | None = None,
+ timeout: float | httpx.Timeout | None | NotGiven = not_given,
+ ) -> LoginSession:
+ """
+ Submit input for the current step of a bridge login session.
+
+ Args:
+ bridge_id: Bridge ID.
+
+ login_session_id: Temporary bridge login session ID.
+
+ step_id: Current bridge login session step ID.
+
+ fields: Field values keyed by the field IDs from the current step.
+
+ last_url: Last browser URL reached during a cookies step, if available.
+
+ source: How the step was completed. Omit unless the client needs to distinguish an
+ embedded webview or browser extension.
+
+ extra_headers: Send extra headers
+
+ extra_query: Add additional query parameters to the request
+
+ extra_body: Add additional JSON properties to the request
+
+ timeout: Override the client-level default timeout for this request, in seconds
+ """
+ if not bridge_id:
+ raise ValueError(f"Expected a non-empty value for `bridge_id` but received {bridge_id!r}")
+ if not login_session_id:
+ raise ValueError(f"Expected a non-empty value for `login_session_id` but received {login_session_id!r}")
+ if not step_id:
+ raise ValueError(f"Expected a non-empty value for `step_id` but received {step_id!r}")
+ return self._post(
+ path_template(
+ "/v1/bridges/{bridge_id}/login-sessions/{login_session_id}/steps/{step_id}",
+ bridge_id=bridge_id,
+ login_session_id=login_session_id,
+ step_id=step_id,
+ ),
+ body=maybe_transform(
+ {
+ "type": type,
+ "fields": fields,
+ "last_url": last_url,
+ "source": source,
+ },
+ step_submit_params.StepSubmitParams,
+ ),
+ options=make_request_options(
+ extra_headers=extra_headers, extra_query=extra_query, extra_body=extra_body, timeout=timeout
+ ),
+ cast_to=LoginSession,
+ )
+
+
+class AsyncStepsResource(AsyncAPIResource):
+ """
+ Available bridges, bridge logins, login sessions for connect and reconnect flows, and advanced network capabilities.
+ """
+
+ @cached_property
+ def with_raw_response(self) -> AsyncStepsResourceWithRawResponse:
+ """
+ This property can be used as a prefix for any HTTP method call to return
+ the raw response object instead of the parsed content.
+
+ For more information, see https://www.github.com/beeper/desktop-api-python#accessing-raw-response-data-eg-headers
+ """
+ return AsyncStepsResourceWithRawResponse(self)
+
+ @cached_property
+ def with_streaming_response(self) -> AsyncStepsResourceWithStreamingResponse:
+ """
+ An alternative to `.with_raw_response` that doesn't eagerly read the response body.
+
+ For more information, see https://www.github.com/beeper/desktop-api-python#with_streaming_response
+ """
+ return AsyncStepsResourceWithStreamingResponse(self)
+
+ async def submit(
+ self,
+ step_id: str,
+ *,
+ bridge_id: str,
+ login_session_id: str,
+ type: Literal["user_input", "cookies", "display_and_wait"],
+ fields: Dict[str, str] | Omit = omit,
+ last_url: str | Omit = omit,
+ source: Literal["api", "webview", "browser_extension"] | Omit = omit,
+ # Use the following arguments if you need to pass additional parameters to the API that aren't available via kwargs.
+ # The extra values given here take precedence over values defined on the client or passed to this method.
+ extra_headers: Headers | None = None,
+ extra_query: Query | None = None,
+ extra_body: Body | None = None,
+ timeout: float | httpx.Timeout | None | NotGiven = not_given,
+ ) -> LoginSession:
+ """
+ Submit input for the current step of a bridge login session.
+
+ Args:
+ bridge_id: Bridge ID.
+
+ login_session_id: Temporary bridge login session ID.
+
+ step_id: Current bridge login session step ID.
+
+ fields: Field values keyed by the field IDs from the current step.
+
+ last_url: Last browser URL reached during a cookies step, if available.
+
+ source: How the step was completed. Omit unless the client needs to distinguish an
+ embedded webview or browser extension.
+
+ extra_headers: Send extra headers
+
+ extra_query: Add additional query parameters to the request
+
+ extra_body: Add additional JSON properties to the request
+
+ timeout: Override the client-level default timeout for this request, in seconds
+ """
+ if not bridge_id:
+ raise ValueError(f"Expected a non-empty value for `bridge_id` but received {bridge_id!r}")
+ if not login_session_id:
+ raise ValueError(f"Expected a non-empty value for `login_session_id` but received {login_session_id!r}")
+ if not step_id:
+ raise ValueError(f"Expected a non-empty value for `step_id` but received {step_id!r}")
+ return await self._post(
+ path_template(
+ "/v1/bridges/{bridge_id}/login-sessions/{login_session_id}/steps/{step_id}",
+ bridge_id=bridge_id,
+ login_session_id=login_session_id,
+ step_id=step_id,
+ ),
+ body=await async_maybe_transform(
+ {
+ "type": type,
+ "fields": fields,
+ "last_url": last_url,
+ "source": source,
+ },
+ step_submit_params.StepSubmitParams,
+ ),
+ options=make_request_options(
+ extra_headers=extra_headers, extra_query=extra_query, extra_body=extra_body, timeout=timeout
+ ),
+ cast_to=LoginSession,
+ )
+
+
+class StepsResourceWithRawResponse:
+ def __init__(self, steps: StepsResource) -> None:
+ self._steps = steps
+
+ self.submit = to_raw_response_wrapper(
+ steps.submit,
+ )
+
+
+class AsyncStepsResourceWithRawResponse:
+ def __init__(self, steps: AsyncStepsResource) -> None:
+ self._steps = steps
+
+ self.submit = async_to_raw_response_wrapper(
+ steps.submit,
+ )
+
+
+class StepsResourceWithStreamingResponse:
+ def __init__(self, steps: StepsResource) -> None:
+ self._steps = steps
+
+ self.submit = to_streamed_response_wrapper(
+ steps.submit,
+ )
+
+
+class AsyncStepsResourceWithStreamingResponse:
+ def __init__(self, steps: AsyncStepsResource) -> None:
+ self._steps = steps
+
+ self.submit = async_to_streamed_response_wrapper(
+ steps.submit,
+ )
diff --git a/src/beeper_desktop_api/resources/chats/chats.py b/src/beeper_desktop_api/resources/chats/chats.py
index 38775e0..66dd5e7 100644
--- a/src/beeper_desktop_api/resources/chats/chats.py
+++ b/src/beeper_desktop_api/resources/chats/chats.py
@@ -157,11 +157,11 @@ def retrieve(
timeout: float | httpx.Timeout | None | NotGiven = not_given,
) -> Chat:
"""
- Retrieve chat details including metadata, participants, and latest message
+ Retrieve chat details, including metadata, participants, and the latest message.
Args:
- chat_id: Chat ID. Input routes also accept the local chat ID from this Beeper Desktop
- installation when available.
+ chat_id: Chat ID. Input routes also accept the local chat ID from this installation when
+ available.
max_participant_count: Maximum number of participants to return. Use -1 for all; otherwise 0-500.
Defaults to 100. List and search endpoints return up to 20 participants per
@@ -213,13 +213,13 @@ def update(
) -> Chat:
"""Update supported chat fields.
- Non-empty draft objects are accepted only when the
+ Non-empty drafts are accepted only when the
current draft is empty. Send draft=null to clear the draft before setting new
draft text or attachments.
Args:
- chat_id: Chat ID. Input routes also accept the local chat ID from this Beeper Desktop
- installation when available.
+ chat_id: Chat ID. Input routes also accept the local chat ID from this installation when
+ available.
description: Group chat description/topic. Support depends on the chat account and chat
permissions.
@@ -343,12 +343,12 @@ def archive(
) -> None:
"""Archive or unarchive a chat.
- Set archived=true to move to archive,
- archived=false to move back to inbox
+ Set archived=true to move it to Archive, or
+ archived=false to move it back to the inbox.
Args:
- chat_id: Chat ID. Input routes also accept the local chat ID from this Beeper Desktop
- installation when available.
+ chat_id: Chat ID. Input routes also accept the local chat ID from this installation when
+ available.
archived: True to archive, false to unarchive
@@ -388,8 +388,8 @@ def mark_read(
Mark a chat as read, optionally through a specific message ID.
Args:
- chat_id: Chat ID. Input routes also accept the local chat ID from this Beeper Desktop
- installation when available.
+ chat_id: Chat ID. Input routes also accept the local chat ID from this installation when
+ available.
message_id: Optional message ID to mark read through.
@@ -428,8 +428,8 @@ def mark_unread(
Mark a chat as unread, optionally from a specific message ID.
Args:
- chat_id: Chat ID. Input routes also accept the local chat ID from this Beeper Desktop
- installation when available.
+ chat_id: Chat ID. Input routes also accept the local chat ID from this installation when
+ available.
message_id: Optional message ID to mark unread from.
@@ -464,12 +464,13 @@ def notify_anyway(
timeout: float | httpx.Timeout | None | NotGiven = not_given,
) -> Chat:
"""
- Force a delivery notification when supported by the underlying network.
- Currently intended for iMessage on macOS; unsupported networks return an error.
+ Send a notification despite the recipient focus state when the network supports
+ it. Currently intended for iMessage on macOS; unsupported networks return an
+ error.
Args:
- chat_id: Chat ID. Input routes also accept the local chat ID from this Beeper Desktop
- installation when available.
+ chat_id: Chat ID. Input routes also accept the local chat ID from this installation when
+ available.
extra_headers: Send extra headers
@@ -483,6 +484,7 @@ def notify_anyway(
raise ValueError(f"Expected a non-empty value for `chat_id` but received {chat_id!r}")
return self._post(
path_template("/v1/chats/{chat_id}/notify-anyway", chat_id=chat_id),
+ body={},
options=make_request_options(
extra_headers=extra_headers, extra_query=extra_query, extra_body=extra_body, timeout=timeout
),
@@ -515,8 +517,7 @@ def search(
Search chats by title, network, or participant names.
Args:
- account_ids: Provide an array of account IDs to filter chats from specific messaging accounts
- only
+ account_ids: Limit results to specific chat accounts.
cursor: Opaque pagination cursor; do not inspect. Use together with 'direction'.
@@ -529,16 +530,14 @@ def search(
include_muted: Include chats marked as Muted by the user, which are usually less important.
Default: true. Set to false if the user wants a more refined search.
- last_activity_after: Provide an ISO datetime string to only retrieve chats with last activity after
- this time
+ last_activity_after: Only include chats with last activity after this ISO 8601 datetime.
- last_activity_before: Provide an ISO datetime string to only retrieve chats with last activity before
- this time
+ last_activity_before: Only include chats with last activity before this ISO 8601 datetime.
limit: Set the maximum number of chats to retrieve. Valid range: 1-200, default is 50
- query: Literal token search (non-semantic). Use single words users type (e.g.,
- "dinner"). When multiple words provided, ALL must match. Case-insensitive.
+ query: Literal chat search. Use words the user typed, such as "dinner". When multiple
+ words are provided, all must match. Case-insensitive.
scope: Search scope: 'titles' matches title + network; 'participants' matches
participant names.
@@ -602,12 +601,12 @@ def start(
"""Resolve a user/contact and open a direct chat.
Reuses and returns an existing
- direct chat when one is found. Available in Beeper Desktop v4.2.808+.
+ direct chat when one is found. Available in Beeper v4.2.808+.
Args:
account_id: Account to create or start the chat on.
- user: Merged user-like contact payload used to resolve the best identifier.
+ user: Contact-like user payload used to resolve the best identifier.
allow_invite: Whether invite-based DM creation is allowed when required by the platform.
@@ -741,11 +740,11 @@ async def retrieve(
timeout: float | httpx.Timeout | None | NotGiven = not_given,
) -> Chat:
"""
- Retrieve chat details including metadata, participants, and latest message
+ Retrieve chat details, including metadata, participants, and the latest message.
Args:
- chat_id: Chat ID. Input routes also accept the local chat ID from this Beeper Desktop
- installation when available.
+ chat_id: Chat ID. Input routes also accept the local chat ID from this installation when
+ available.
max_participant_count: Maximum number of participants to return. Use -1 for all; otherwise 0-500.
Defaults to 100. List and search endpoints return up to 20 participants per
@@ -797,13 +796,13 @@ async def update(
) -> Chat:
"""Update supported chat fields.
- Non-empty draft objects are accepted only when the
+ Non-empty drafts are accepted only when the
current draft is empty. Send draft=null to clear the draft before setting new
draft text or attachments.
Args:
- chat_id: Chat ID. Input routes also accept the local chat ID from this Beeper Desktop
- installation when available.
+ chat_id: Chat ID. Input routes also accept the local chat ID from this installation when
+ available.
description: Group chat description/topic. Support depends on the chat account and chat
permissions.
@@ -927,12 +926,12 @@ async def archive(
) -> None:
"""Archive or unarchive a chat.
- Set archived=true to move to archive,
- archived=false to move back to inbox
+ Set archived=true to move it to Archive, or
+ archived=false to move it back to the inbox.
Args:
- chat_id: Chat ID. Input routes also accept the local chat ID from this Beeper Desktop
- installation when available.
+ chat_id: Chat ID. Input routes also accept the local chat ID from this installation when
+ available.
archived: True to archive, false to unarchive
@@ -972,8 +971,8 @@ async def mark_read(
Mark a chat as read, optionally through a specific message ID.
Args:
- chat_id: Chat ID. Input routes also accept the local chat ID from this Beeper Desktop
- installation when available.
+ chat_id: Chat ID. Input routes also accept the local chat ID from this installation when
+ available.
message_id: Optional message ID to mark read through.
@@ -1012,8 +1011,8 @@ async def mark_unread(
Mark a chat as unread, optionally from a specific message ID.
Args:
- chat_id: Chat ID. Input routes also accept the local chat ID from this Beeper Desktop
- installation when available.
+ chat_id: Chat ID. Input routes also accept the local chat ID from this installation when
+ available.
message_id: Optional message ID to mark unread from.
@@ -1048,12 +1047,13 @@ async def notify_anyway(
timeout: float | httpx.Timeout | None | NotGiven = not_given,
) -> Chat:
"""
- Force a delivery notification when supported by the underlying network.
- Currently intended for iMessage on macOS; unsupported networks return an error.
+ Send a notification despite the recipient focus state when the network supports
+ it. Currently intended for iMessage on macOS; unsupported networks return an
+ error.
Args:
- chat_id: Chat ID. Input routes also accept the local chat ID from this Beeper Desktop
- installation when available.
+ chat_id: Chat ID. Input routes also accept the local chat ID from this installation when
+ available.
extra_headers: Send extra headers
@@ -1067,6 +1067,7 @@ async def notify_anyway(
raise ValueError(f"Expected a non-empty value for `chat_id` but received {chat_id!r}")
return await self._post(
path_template("/v1/chats/{chat_id}/notify-anyway", chat_id=chat_id),
+ body={},
options=make_request_options(
extra_headers=extra_headers, extra_query=extra_query, extra_body=extra_body, timeout=timeout
),
@@ -1099,8 +1100,7 @@ def search(
Search chats by title, network, or participant names.
Args:
- account_ids: Provide an array of account IDs to filter chats from specific messaging accounts
- only
+ account_ids: Limit results to specific chat accounts.
cursor: Opaque pagination cursor; do not inspect. Use together with 'direction'.
@@ -1113,16 +1113,14 @@ def search(
include_muted: Include chats marked as Muted by the user, which are usually less important.
Default: true. Set to false if the user wants a more refined search.
- last_activity_after: Provide an ISO datetime string to only retrieve chats with last activity after
- this time
+ last_activity_after: Only include chats with last activity after this ISO 8601 datetime.
- last_activity_before: Provide an ISO datetime string to only retrieve chats with last activity before
- this time
+ last_activity_before: Only include chats with last activity before this ISO 8601 datetime.
limit: Set the maximum number of chats to retrieve. Valid range: 1-200, default is 50
- query: Literal token search (non-semantic). Use single words users type (e.g.,
- "dinner"). When multiple words provided, ALL must match. Case-insensitive.
+ query: Literal chat search. Use words the user typed, such as "dinner". When multiple
+ words are provided, all must match. Case-insensitive.
scope: Search scope: 'titles' matches title + network; 'participants' matches
participant names.
@@ -1186,12 +1184,12 @@ async def start(
"""Resolve a user/contact and open a direct chat.
Reuses and returns an existing
- direct chat when one is found. Available in Beeper Desktop v4.2.808+.
+ direct chat when one is found. Available in Beeper v4.2.808+.
Args:
account_id: Account to create or start the chat on.
- user: Merged user-like contact payload used to resolve the best identifier.
+ user: Contact-like user payload used to resolve the best identifier.
allow_invite: Whether invite-based DM creation is allowed when required by the platform.
diff --git a/src/beeper_desktop_api/resources/chats/messages/reactions.py b/src/beeper_desktop_api/resources/chats/messages/reactions.py
index 974d9f6..c9199da 100644
--- a/src/beeper_desktop_api/resources/chats/messages/reactions.py
+++ b/src/beeper_desktop_api/resources/chats/messages/reactions.py
@@ -61,8 +61,8 @@ def delete(
Remove the reaction added by the authenticated user from an existing message.
Args:
- chat_id: Chat ID. Input routes also accept the local chat ID from this Beeper Desktop
- installation when available.
+ chat_id: Chat ID. Input routes also accept the local chat ID from this installation when
+ available.
message_id: Message ID.
@@ -114,8 +114,8 @@ def add(
Args:
chat_id: Chat ID.
- Input routes also accept the local chat ID from this Beeper Desktop
- installation when available.
+ Input routes also accept the local chat ID from this installation when
+ available.
message_id: Message ID.
@@ -192,8 +192,8 @@ async def delete(
Remove the reaction added by the authenticated user from an existing message.
Args:
- chat_id: Chat ID. Input routes also accept the local chat ID from this Beeper Desktop
- installation when available.
+ chat_id: Chat ID. Input routes also accept the local chat ID from this installation when
+ available.
message_id: Message ID.
@@ -245,8 +245,8 @@ async def add(
Args:
chat_id: Chat ID.
- Input routes also accept the local chat ID from this Beeper Desktop
- installation when available.
+ Input routes also accept the local chat ID from this installation when
+ available.
message_id: Message ID.
diff --git a/src/beeper_desktop_api/resources/chats/reminders.py b/src/beeper_desktop_api/resources/chats/reminders.py
index ef502a6..fb02508 100644
--- a/src/beeper_desktop_api/resources/chats/reminders.py
+++ b/src/beeper_desktop_api/resources/chats/reminders.py
@@ -54,13 +54,13 @@ def create(
extra_body: Body | None = None,
timeout: float | httpx.Timeout | None | NotGiven = not_given,
) -> None:
- """Set a reminder for a chat at a specific time
+ """Set a reminder for a chat at a specific time.
Args:
chat_id: Chat ID.
- Input routes also accept the local chat ID from this Beeper Desktop
- installation when available.
+ Input routes also accept the local chat ID from this installation when
+ available.
reminder: Reminder configuration
@@ -95,13 +95,13 @@ def delete(
extra_body: Body | None = None,
timeout: float | httpx.Timeout | None | NotGiven = not_given,
) -> None:
- """Clear an existing reminder from a chat
+ """Clear an existing reminder from a chat.
Args:
chat_id: Chat ID.
- Input routes also accept the local chat ID from this Beeper Desktop
- installation when available.
+ Input routes also accept the local chat ID from this installation when
+ available.
extra_headers: Send extra headers
@@ -157,13 +157,13 @@ async def create(
extra_body: Body | None = None,
timeout: float | httpx.Timeout | None | NotGiven = not_given,
) -> None:
- """Set a reminder for a chat at a specific time
+ """Set a reminder for a chat at a specific time.
Args:
chat_id: Chat ID.
- Input routes also accept the local chat ID from this Beeper Desktop
- installation when available.
+ Input routes also accept the local chat ID from this installation when
+ available.
reminder: Reminder configuration
@@ -198,13 +198,13 @@ async def delete(
extra_body: Body | None = None,
timeout: float | httpx.Timeout | None | NotGiven = not_given,
) -> None:
- """Clear an existing reminder from a chat
+ """Clear an existing reminder from a chat.
Args:
chat_id: Chat ID.
- Input routes also accept the local chat ID from this Beeper Desktop
- installation when available.
+ Input routes also accept the local chat ID from this installation when
+ available.
extra_headers: Send extra headers
diff --git a/src/beeper_desktop_api/resources/info.py b/src/beeper_desktop_api/resources/info.py
index d7eaf8d..c642841 100644
--- a/src/beeper_desktop_api/resources/info.py
+++ b/src/beeper_desktop_api/resources/info.py
@@ -56,7 +56,7 @@ def retrieve(
) -> InfoRetrieveResponse:
"""
Returns app, platform, server, endpoint discovery, OAuth, and WebSocket metadata
- for this Beeper Desktop instance.
+ for this Beeper Client API server.
"""
return self._get(
"/v1/info",
@@ -108,7 +108,7 @@ async def retrieve(
) -> InfoRetrieveResponse:
"""
Returns app, platform, server, endpoint discovery, OAuth, and WebSocket metadata
- for this Beeper Desktop instance.
+ for this Beeper Client API server.
"""
return await self._get(
"/v1/info",
diff --git a/src/beeper_desktop_api/resources/messages.py b/src/beeper_desktop_api/resources/messages.py
index 290c923..7c4e7e6 100644
--- a/src/beeper_desktop_api/resources/messages.py
+++ b/src/beeper_desktop_api/resources/messages.py
@@ -70,11 +70,11 @@ def retrieve(
) -> Message:
"""
Retrieve a message by final message ID, pendingMessageID, or Matrix event ID.
- Chat ID may be a Beeper chat ID or local chat ID.
+ chatID may be a Beeper chat ID or a local chat ID.
Args:
- chat_id: Chat ID. Input routes also accept the local chat ID from this Beeper Desktop
- installation when available.
+ chat_id: Chat ID. Input routes also accept the local chat ID from this installation when
+ available.
message_id: Message ID.
@@ -117,8 +117,8 @@ def update(
be edited.
Args:
- chat_id: Chat ID. Input routes also accept the local chat ID from this Beeper Desktop
- installation when available.
+ chat_id: Chat ID. Input routes also accept the local chat ID from this installation when
+ available.
message_id: Message ID.
@@ -163,8 +163,8 @@ def list(
Sorted by timestamp.
Args:
- chat_id: Chat ID. Input routes also accept the local chat ID from this Beeper Desktop
- installation when available.
+ chat_id: Chat ID. Input routes also accept the local chat ID from this installation when
+ available.
cursor: Opaque pagination cursor; do not inspect. Use together with 'direction'.
@@ -219,8 +219,8 @@ def delete(
because messages cannot be deleted while sending.
Args:
- chat_id: Chat ID. Input routes also accept the local chat ID from this Beeper Desktop
- installation when available.
+ chat_id: Chat ID. Input routes also accept the local chat ID from this installation when
+ available.
message_id: Message ID.
@@ -307,10 +307,10 @@ def search(
media_types: Filter messages by media types. Use ['any'] for any media type, or specify exact
types like ['video', 'image']. Omit for no media filtering.
- query: Literal word search (non-semantic). Finds messages containing these EXACT words
- in any order. Use single words users actually type, not concepts or phrases.
- Example: use "dinner" not "dinner plans", use "sick" not "health issues". If
- omitted, returns results filtered only by other parameters.
+ query: Literal word search. Finds messages containing these words in any order. Use
+ words the user actually typed, not inferred concepts. Example: use "dinner"
+ rather than "dinner plans". If omitted, returns results filtered only by the
+ other parameters.
sender: Filter by sender: 'me' (messages sent by the authenticated user), 'others'
(messages sent by others), or a specific user ID string (user.id).
@@ -373,15 +373,15 @@ def send(
Returns a pending message ID.
Args:
- chat_id: Chat ID. Input routes also accept the local chat ID from this Beeper Desktop
- installation when available.
+ chat_id: Chat ID. Input routes also accept the local chat ID from this installation when
+ available.
attachment: Single attachment to send with the message
reply_to_message_id: Provide a message ID to send this as a reply to an existing message
- text: Draft text. Plain text and Markdown are converted to Matrix HTML with the same
- rules used by send and edit.
+ text: Draft text. Plain text and Markdown are converted to Beeper rich text with the
+ same rules used by send and edit.
extra_headers: Send extra headers
@@ -446,11 +446,11 @@ async def retrieve(
) -> Message:
"""
Retrieve a message by final message ID, pendingMessageID, or Matrix event ID.
- Chat ID may be a Beeper chat ID or local chat ID.
+ chatID may be a Beeper chat ID or a local chat ID.
Args:
- chat_id: Chat ID. Input routes also accept the local chat ID from this Beeper Desktop
- installation when available.
+ chat_id: Chat ID. Input routes also accept the local chat ID from this installation when
+ available.
message_id: Message ID.
@@ -493,8 +493,8 @@ async def update(
be edited.
Args:
- chat_id: Chat ID. Input routes also accept the local chat ID from this Beeper Desktop
- installation when available.
+ chat_id: Chat ID. Input routes also accept the local chat ID from this installation when
+ available.
message_id: Message ID.
@@ -539,8 +539,8 @@ def list(
Sorted by timestamp.
Args:
- chat_id: Chat ID. Input routes also accept the local chat ID from this Beeper Desktop
- installation when available.
+ chat_id: Chat ID. Input routes also accept the local chat ID from this installation when
+ available.
cursor: Opaque pagination cursor; do not inspect. Use together with 'direction'.
@@ -595,8 +595,8 @@ async def delete(
because messages cannot be deleted while sending.
Args:
- chat_id: Chat ID. Input routes also accept the local chat ID from this Beeper Desktop
- installation when available.
+ chat_id: Chat ID. Input routes also accept the local chat ID from this installation when
+ available.
message_id: Message ID.
@@ -685,10 +685,10 @@ def search(
media_types: Filter messages by media types. Use ['any'] for any media type, or specify exact
types like ['video', 'image']. Omit for no media filtering.
- query: Literal word search (non-semantic). Finds messages containing these EXACT words
- in any order. Use single words users actually type, not concepts or phrases.
- Example: use "dinner" not "dinner plans", use "sick" not "health issues". If
- omitted, returns results filtered only by other parameters.
+ query: Literal word search. Finds messages containing these words in any order. Use
+ words the user actually typed, not inferred concepts. Example: use "dinner"
+ rather than "dinner plans". If omitted, returns results filtered only by the
+ other parameters.
sender: Filter by sender: 'me' (messages sent by the authenticated user), 'others'
(messages sent by others), or a specific user ID string (user.id).
@@ -751,15 +751,15 @@ async def send(
Returns a pending message ID.
Args:
- chat_id: Chat ID. Input routes also accept the local chat ID from this Beeper Desktop
- installation when available.
+ chat_id: Chat ID. Input routes also accept the local chat ID from this installation when
+ available.
attachment: Single attachment to send with the message
reply_to_message_id: Provide a message ID to send this as a reply to an existing message
- text: Draft text. Plain text and Markdown are converted to Matrix HTML with the same
- rules used by send and edit.
+ text: Draft text. Plain text and Markdown are converted to Beeper rich text with the
+ same rules used by send and edit.
extra_headers: Send extra headers
diff --git a/src/beeper_desktop_api/types/__init__.py b/src/beeper_desktop_api/types/__init__.py
index 78ec780..137c214 100644
--- a/src/beeper_desktop_api/types/__init__.py
+++ b/src/beeper_desktop_api/types/__init__.py
@@ -3,12 +3,26 @@
from __future__ import annotations
from .chat import Chat as Chat
-from .shared import User as User, Error as Error, Message as Message, Reaction as Reaction, Attachment as Attachment
+from .bridge import Bridge as Bridge
+from .shared import (
+ User as User,
+ Error as Error,
+ Message as Message,
+ APIError as APIError,
+ Reaction as Reaction,
+ Attachment as Attachment,
+ AppStateSnapshot as AppStateSnapshot,
+)
from .account import Account as Account
+from .login_flow import LoginFlow as LoginFlow
+from .cookie_field import CookieField as CookieField
+from .login_session import LoginSession as LoginSession
+from .account_bridge import AccountBridge as AccountBridge
from .focus_response import FocusResponse as FocusResponse
from .search_response import SearchResponse as SearchResponse
from .chat_list_params import ChatListParams as ChatListParams
from .chat_start_params import ChatStartParams as ChatStartParams
+from .login_input_field import LoginInputField as LoginInputField
from .asset_serve_params import AssetServeParams as AssetServeParams
from .chat_create_params import ChatCreateParams as ChatCreateParams
from .chat_list_response import ChatListResponse as ChatListResponse
@@ -20,6 +34,8 @@
from .client_focus_params import ClientFocusParams as ClientFocusParams
from .message_list_params import MessageListParams as MessageListParams
from .message_send_params import MessageSendParams as MessageSendParams
+from .app_session_response import AppSessionResponse as AppSessionResponse
+from .bridge_list_response import BridgeListResponse as BridgeListResponse
from .chat_create_response import ChatCreateResponse as ChatCreateResponse
from .chat_retrieve_params import ChatRetrieveParams as ChatRetrieveParams
from .client_search_params import ClientSearchParams as ClientSearchParams
@@ -31,9 +47,16 @@
from .message_search_params import MessageSearchParams as MessageSearchParams
from .message_send_response import MessageSendResponse as MessageSendResponse
from .message_update_params import MessageUpdateParams as MessageUpdateParams
+from .group_field_capability import GroupFieldCapability as GroupFieldCapability
from .info_retrieve_response import InfoRetrieveResponse as InfoRetrieveResponse
from .asset_download_response import AssetDownloadResponse as AssetDownloadResponse
from .chat_mark_unread_params import ChatMarkUnreadParams as ChatMarkUnreadParams
+from .group_type_capabilities import GroupTypeCapabilities as GroupTypeCapabilities
from .message_update_response import MessageUpdateResponse as MessageUpdateResponse
+from .bridge_retrieve_response import BridgeRetrieveResponse as BridgeRetrieveResponse
+from .account_retrieve_response import AccountRetrieveResponse as AccountRetrieveResponse
+from .provisioning_capabilities import ProvisioningCapabilities as ProvisioningCapabilities
from .asset_upload_base64_params import AssetUploadBase64Params as AssetUploadBase64Params
from .asset_upload_base64_response import AssetUploadBase64Response as AssetUploadBase64Response
+from .disappearing_timer_capability import DisappearingTimerCapability as DisappearingTimerCapability
+from .resolve_identifier_capabilities import ResolveIdentifierCapabilities as ResolveIdentifierCapabilities
diff --git a/src/beeper_desktop_api/types/account.py b/src/beeper_desktop_api/types/account.py
index fdc1a4b..a469ec4 100644
--- a/src/beeper_desktop_api/types/account.py
+++ b/src/beeper_desktop_api/types/account.py
@@ -1,35 +1,15 @@
# File generated from our OpenAPI spec by Stainless. See CONTRIBUTING.md for details.
-from typing import Optional
+from typing import Dict, Optional
from typing_extensions import Literal
from pydantic import Field as FieldInfo
from .._models import BaseModel
from .shared.user import User
+from .account_bridge import AccountBridge
-__all__ = ["Account", "Bridge"]
-
-
-class Bridge(BaseModel):
- """Bridge metadata for the account. Available in Beeper Desktop v4.2.785+."""
-
- id: str
- """Bridge instance identifier.
-
- Matrix and cloud bridges often use the bridge type (for example matrix or
- discordgo); local bridges use a local bridge ID (for example local-whatsapp).
- Available in Beeper Desktop v4.2.785+.
- """
-
- provider: Literal["cloud", "self-hosted", "local", "platform-sdk"]
- """Bridge provider for the account. Available in Beeper Desktop v4.2.785+."""
-
- type: str
- """Bridge type, such as matrix, discordgo, slackgo, whatsapp, telegram, or twitter.
-
- Available in Beeper Desktop v4.2.785+.
- """
+__all__ = ["Account"]
class Account(BaseModel):
@@ -43,14 +23,38 @@ class Account(BaseModel):
workspace-scoped cloud bridges, and local-whatsapp*ba*... for local bridges.
"""
- bridge: Bridge
+ bridge: AccountBridge
"""Bridge metadata for the account. Available in Beeper Desktop v4.2.785+."""
+ status: Literal[
+ "connected",
+ "connecting",
+ "backfilling",
+ "connection_required",
+ "reconnect_required",
+ "attention_required",
+ "disconnected",
+ "disabled",
+ ]
+ """Current connection status for this account."""
+
user: User
"""User the account belongs to."""
+ capabilities: Optional[Dict[str, Optional[object]]] = None
+ """Runtime chat/message capabilities for this connected account, when available."""
+
+ login_id: Optional[str] = FieldInfo(alias="loginID", default=None)
+ """Bridge login ID for this account, when known.
+
+ One bridge login can contain multiple chat accounts.
+ """
+
network: Optional[str] = None
"""Human-friendly network name for the account.
Omitted when the network is unknown.
"""
+
+ status_text: Optional[str] = FieldInfo(alias="statusText", default=None)
+ """Human-friendly account status text."""
diff --git a/src/beeper_desktop_api/types/account_bridge.py b/src/beeper_desktop_api/types/account_bridge.py
new file mode 100644
index 0000000..d507424
--- /dev/null
+++ b/src/beeper_desktop_api/types/account_bridge.py
@@ -0,0 +1,31 @@
+# File generated from our OpenAPI spec by Stainless. See CONTRIBUTING.md for details.
+
+from typing_extensions import Literal
+
+from .._models import BaseModel
+
+__all__ = ["AccountBridge"]
+
+
+class AccountBridge(BaseModel):
+ """Bridge metadata for the account. Available in Beeper Desktop v4.2.785+."""
+
+ id: str
+ """Bridge identifier.
+
+ Beeper Cloud accounts often use the network type (for example matrix or
+ discordgo); on-device accounts use a local bridge ID (for example
+ local-whatsapp). Available in Beeper Desktop v4.2.785+.
+ """
+
+ provider: Literal["cloud", "self-hosted", "local", "platform-sdk"]
+ """Where this account runs: on this device or in Beeper Cloud.
+
+ Available in Beeper Desktop v4.2.785+.
+ """
+
+ type: str
+ """Bridge type, such as matrix, discordgo, slackgo, whatsapp, telegram, or twitter.
+
+ Available in Beeper Desktop v4.2.785+.
+ """
diff --git a/src/beeper_desktop_api/types/account_retrieve_response.py b/src/beeper_desktop_api/types/account_retrieve_response.py
new file mode 100644
index 0000000..20b59e8
--- /dev/null
+++ b/src/beeper_desktop_api/types/account_retrieve_response.py
@@ -0,0 +1,60 @@
+# File generated from our OpenAPI spec by Stainless. See CONTRIBUTING.md for details.
+
+from typing import Dict, Optional
+from typing_extensions import Literal
+
+from pydantic import Field as FieldInfo
+
+from .._models import BaseModel
+from .shared.user import User
+from .account_bridge import AccountBridge
+
+__all__ = ["AccountRetrieveResponse"]
+
+
+class AccountRetrieveResponse(BaseModel):
+ """A chat account added to Beeper."""
+
+ account_id: str = FieldInfo(alias="accountID")
+ """Chat account added to Beeper.
+
+ Use this to route account-scoped actions. Examples include matrix for
+ Beeper/Matrix, discordgo for a cloud bridge, slackgo.TEAM-USER for
+ workspace-scoped cloud bridges, and local-whatsapp*ba*... for local bridges.
+ """
+
+ bridge: AccountBridge
+ """Bridge metadata for the account. Available in Beeper Desktop v4.2.785+."""
+
+ status: Literal[
+ "connected",
+ "connecting",
+ "backfilling",
+ "connection_required",
+ "reconnect_required",
+ "attention_required",
+ "disconnected",
+ "disabled",
+ ]
+ """Current connection status for this account."""
+
+ user: User
+ """User the account belongs to."""
+
+ capabilities: Optional[Dict[str, Optional[object]]] = None
+ """Runtime chat/message capabilities for this connected account, when available."""
+
+ login_id: Optional[str] = FieldInfo(alias="loginID", default=None)
+ """Bridge login ID for this account, when known.
+
+ One bridge login can contain multiple chat accounts.
+ """
+
+ network: Optional[str] = None
+ """Human-friendly network name for the account.
+
+ Omitted when the network is unknown.
+ """
+
+ status_text: Optional[str] = FieldInfo(alias="statusText", default=None)
+ """Human-friendly account status text."""
diff --git a/src/beeper_desktop_api/types/accounts/contact_list_params.py b/src/beeper_desktop_api/types/accounts/contact_list_params.py
index e452ddb..983aea0 100644
--- a/src/beeper_desktop_api/types/accounts/contact_list_params.py
+++ b/src/beeper_desktop_api/types/accounts/contact_list_params.py
@@ -21,4 +21,4 @@ class ContactListParams(TypedDict, total=False):
"""Maximum contacts to return per page."""
query: str
- """Optional search query for blended contact lookup."""
+ """Optional search query for contact lookup."""
diff --git a/src/beeper_desktop_api/types/accounts/contact_search_params.py b/src/beeper_desktop_api/types/accounts/contact_search_params.py
index f9063e0..b663175 100644
--- a/src/beeper_desktop_api/types/accounts/contact_search_params.py
+++ b/src/beeper_desktop_api/types/accounts/contact_search_params.py
@@ -9,4 +9,4 @@
class ContactSearchParams(TypedDict, total=False):
query: Required[str]
- """Text to search users by. Network-specific behavior."""
+ """Text to search contacts by. Matching behavior depends on the network."""
diff --git a/src/beeper_desktop_api/types/app/__init__.py b/src/beeper_desktop_api/types/app/__init__.py
new file mode 100644
index 0000000..2944080
--- /dev/null
+++ b/src/beeper_desktop_api/types/app/__init__.py
@@ -0,0 +1,17 @@
+# File generated from our OpenAPI spec by Stainless. See CONTRIBUTING.md for details.
+
+from __future__ import annotations
+
+from .login_email_params import LoginEmailParams as LoginEmailParams
+from .login_start_response import LoginStartResponse as LoginStartResponse
+from .login_register_params import LoginRegisterParams as LoginRegisterParams
+from .login_response_params import LoginResponseParams as LoginResponseParams
+from .login_register_response import LoginRegisterResponse as LoginRegisterResponse
+from .login_response_response import LoginResponseResponse as LoginResponseResponse
+from .verification_cancel_params import VerificationCancelParams as VerificationCancelParams
+from .verification_create_params import VerificationCreateParams as VerificationCreateParams
+from .verification_list_response import VerificationListResponse as VerificationListResponse
+from .verification_accept_response import VerificationAcceptResponse as VerificationAcceptResponse
+from .verification_cancel_response import VerificationCancelResponse as VerificationCancelResponse
+from .verification_create_response import VerificationCreateResponse as VerificationCreateResponse
+from .verification_retrieve_response import VerificationRetrieveResponse as VerificationRetrieveResponse
diff --git a/src/beeper_desktop_api/types/app/login/__init__.py b/src/beeper_desktop_api/types/app/login/__init__.py
new file mode 100644
index 0000000..f8ee8b1
--- /dev/null
+++ b/src/beeper_desktop_api/types/app/login/__init__.py
@@ -0,0 +1,3 @@
+# File generated from our OpenAPI spec by Stainless. See CONTRIBUTING.md for details.
+
+from __future__ import annotations
diff --git a/src/beeper_desktop_api/types/app/login/verification/__init__.py b/src/beeper_desktop_api/types/app/login/verification/__init__.py
new file mode 100644
index 0000000..d979259
--- /dev/null
+++ b/src/beeper_desktop_api/types/app/login/verification/__init__.py
@@ -0,0 +1,6 @@
+# File generated from our OpenAPI spec by Stainless. See CONTRIBUTING.md for details.
+
+from __future__ import annotations
+
+from .recovery_key_verify_params import RecoveryKeyVerifyParams as RecoveryKeyVerifyParams
+from .recovery_key_verify_response import RecoveryKeyVerifyResponse as RecoveryKeyVerifyResponse
diff --git a/src/beeper_desktop_api/types/app/login/verification/recovery_key/__init__.py b/src/beeper_desktop_api/types/app/login/verification/recovery_key/__init__.py
new file mode 100644
index 0000000..0299c46
--- /dev/null
+++ b/src/beeper_desktop_api/types/app/login/verification/recovery_key/__init__.py
@@ -0,0 +1,8 @@
+# File generated from our OpenAPI spec by Stainless. See CONTRIBUTING.md for details.
+
+from __future__ import annotations
+
+from .reset_create_params import ResetCreateParams as ResetCreateParams
+from .reset_confirm_params import ResetConfirmParams as ResetConfirmParams
+from .reset_create_response import ResetCreateResponse as ResetCreateResponse
+from .reset_confirm_response import ResetConfirmResponse as ResetConfirmResponse
diff --git a/src/beeper_desktop_api/types/app/login/verification/recovery_key/reset_confirm_params.py b/src/beeper_desktop_api/types/app/login/verification/recovery_key/reset_confirm_params.py
new file mode 100644
index 0000000..8c5ce40
--- /dev/null
+++ b/src/beeper_desktop_api/types/app/login/verification/recovery_key/reset_confirm_params.py
@@ -0,0 +1,14 @@
+# File generated from our OpenAPI spec by Stainless. See CONTRIBUTING.md for details.
+
+from __future__ import annotations
+
+from typing_extensions import Required, Annotated, TypedDict
+
+from ......_utils import PropertyInfo
+
+__all__ = ["ResetConfirmParams"]
+
+
+class ResetConfirmParams(TypedDict, total=False):
+ recovery_key: Required[Annotated[str, PropertyInfo(alias="recoveryKey")]]
+ """New recovery key returned by the reset step."""
diff --git a/src/beeper_desktop_api/types/app/login/verification/recovery_key/reset_confirm_response.py b/src/beeper_desktop_api/types/app/login/verification/recovery_key/reset_confirm_response.py
new file mode 100644
index 0000000..bc6cb66
--- /dev/null
+++ b/src/beeper_desktop_api/types/app/login/verification/recovery_key/reset_confirm_response.py
@@ -0,0 +1,192 @@
+# File generated from our OpenAPI spec by Stainless. See CONTRIBUTING.md for details.
+
+from typing import List, Optional
+from typing_extensions import Literal
+
+from pydantic import Field as FieldInfo
+
+from ......_models import BaseModel
+
+__all__ = [
+ "ResetConfirmResponse",
+ "Session",
+ "SessionE2EE",
+ "SessionE2EESecrets",
+ "SessionMatrix",
+ "SessionVerification",
+ "SessionVerificationError",
+ "SessionVerificationOtherDevice",
+ "SessionVerificationQr",
+ "SessionVerificationSAS",
+]
+
+
+class SessionE2EESecrets(BaseModel):
+ """Encrypted messaging keys available on this device."""
+
+ master_key: bool = FieldInfo(alias="masterKey")
+ """Whether the account identity key is available."""
+
+ megolm_backup_key: bool = FieldInfo(alias="megolmBackupKey")
+ """Whether the encrypted message backup key is available."""
+
+ recovery_key: bool = FieldInfo(alias="recoveryKey")
+ """Whether a recovery key is available."""
+
+ self_signing_key: bool = FieldInfo(alias="selfSigningKey")
+ """Whether the device trust key is available."""
+
+ user_signing_key: bool = FieldInfo(alias="userSigningKey")
+ """Whether the user trust key is available."""
+
+
+class SessionE2EE(BaseModel):
+ """Encrypted messaging setup status."""
+
+ cross_signing: bool = FieldInfo(alias="crossSigning")
+ """Whether this account can verify trusted devices."""
+
+ first_sync_done: bool = FieldInfo(alias="firstSyncDone")
+ """Whether the first encrypted message sync is complete."""
+
+ has_backed_up_recovery_key: bool = FieldInfo(alias="hasBackedUpRecoveryKey")
+ """Whether the user confirmed that they saved their recovery key."""
+
+ initialized: bool
+ """Whether encrypted messaging setup has started."""
+
+ key_backup: bool = FieldInfo(alias="keyBackup")
+ """Whether encrypted message backup is available."""
+
+ secrets: SessionE2EESecrets
+ """Encrypted messaging keys available on this device."""
+
+ secret_storage: bool = FieldInfo(alias="secretStorage")
+ """Whether secure key storage is available."""
+
+ verified: bool
+ """Whether this device is trusted for encrypted messages."""
+
+ recovery_key_generated_at: Optional[float] = FieldInfo(alias="recoveryKeyGeneratedAt", default=None)
+ """Unix timestamp for when the recovery key was created."""
+
+
+class SessionMatrix(BaseModel):
+ """Signed-in account details. Omitted until sign-in is complete."""
+
+ device_id: str = FieldInfo(alias="deviceID")
+ """Current device ID."""
+
+ homeserver: str
+ """Beeper homeserver URL for this account."""
+
+ user_id: str = FieldInfo(alias="userID")
+ """Signed-in Beeper user ID."""
+
+
+class SessionVerificationError(BaseModel):
+ """Verification error details, if verification stopped."""
+
+ code: str
+ """Verification error code."""
+
+ reason: str
+ """User-facing verification error message."""
+
+
+class SessionVerificationOtherDevice(BaseModel):
+ """Other device participating in verification."""
+
+ id: str
+ """Other device ID."""
+
+ name: Optional[str] = None
+ """Other device display name, if known."""
+
+
+class SessionVerificationQr(BaseModel):
+ """QR verification data."""
+
+ data: str
+ """QR code payload to display for verification."""
+
+
+class SessionVerificationSAS(BaseModel):
+ """Emoji or number comparison data for verification."""
+
+ emojis: str
+ """Emoji sequence to compare on both devices."""
+
+ decimals: Optional[str] = None
+ """Number sequence to compare on both devices."""
+
+
+class SessionVerification(BaseModel):
+ """Trusted device verification progress."""
+
+ id: str
+ """Verification ID to pass in verification action paths."""
+
+ available_actions: List[Literal["accept", "cancel", "qr.confirmScanned", "sas.start", "sas.confirm"]] = FieldInfo(
+ alias="availableActions"
+ )
+ """Verification actions that are valid for the current state."""
+
+ direction: Literal["incoming", "outgoing"]
+ """Whether this device started or received the verification."""
+
+ methods: List[Literal["qr", "sas"]]
+ """Verification methods supported for this transaction."""
+
+ purpose: Literal["login", "device"]
+ """Why this verification exists."""
+
+ state: Literal["requested", "ready", "sas_ready", "qr_scanned", "done", "cancelled", "error"]
+ """Current trusted-device verification state."""
+
+ error: Optional[SessionVerificationError] = None
+ """Verification error details, if verification stopped."""
+
+ other_device: Optional[SessionVerificationOtherDevice] = FieldInfo(alias="otherDevice", default=None)
+ """Other device participating in verification."""
+
+ other_user_id: Optional[str] = FieldInfo(alias="otherUserID", default=None)
+ """Other Beeper user participating in verification."""
+
+ qr: Optional[SessionVerificationQr] = None
+ """QR verification data."""
+
+ sas: Optional[SessionVerificationSAS] = None
+ """Emoji or number comparison data for verification."""
+
+
+class Session(BaseModel):
+ """Current app sign-in and encrypted messaging setup state."""
+
+ e2ee: SessionE2EE
+ """Encrypted messaging setup status."""
+
+ state: Literal[
+ "needs-login",
+ "initializing",
+ "needs-cross-signing-setup",
+ "needs-verification",
+ "needs-secrets",
+ "needs-first-sync",
+ "ready",
+ ]
+ """
+ Current sign-in and encrypted messaging setup state for Beeper Desktop or Beeper
+ Server.
+ """
+
+ matrix: Optional[SessionMatrix] = None
+ """Signed-in account details. Omitted until sign-in is complete."""
+
+ verification: Optional[SessionVerification] = None
+ """Trusted device verification progress."""
+
+
+class ResetConfirmResponse(BaseModel):
+ session: Session
+ """Current app sign-in and encrypted messaging setup state."""
diff --git a/src/beeper_desktop_api/types/app/login/verification/recovery_key/reset_create_params.py b/src/beeper_desktop_api/types/app/login/verification/recovery_key/reset_create_params.py
new file mode 100644
index 0000000..5d9fd5b
--- /dev/null
+++ b/src/beeper_desktop_api/types/app/login/verification/recovery_key/reset_create_params.py
@@ -0,0 +1,14 @@
+# File generated from our OpenAPI spec by Stainless. See CONTRIBUTING.md for details.
+
+from __future__ import annotations
+
+from typing_extensions import Annotated, TypedDict
+
+from ......_utils import PropertyInfo
+
+__all__ = ["ResetCreateParams"]
+
+
+class ResetCreateParams(TypedDict, total=False):
+ existing_recovery_key: Annotated[str, PropertyInfo(alias="existingRecoveryKey")]
+ """Existing recovery key, if the user has it."""
diff --git a/src/beeper_desktop_api/types/app/login/verification/recovery_key/reset_create_response.py b/src/beeper_desktop_api/types/app/login/verification/recovery_key/reset_create_response.py
new file mode 100644
index 0000000..166f642
--- /dev/null
+++ b/src/beeper_desktop_api/types/app/login/verification/recovery_key/reset_create_response.py
@@ -0,0 +1,200 @@
+# File generated from our OpenAPI spec by Stainless. See CONTRIBUTING.md for details.
+
+from typing import List, Optional
+from typing_extensions import Literal
+
+from pydantic import Field as FieldInfo
+
+from ......_models import BaseModel
+
+__all__ = [
+ "ResetCreateResponse",
+ "Session",
+ "SessionE2EE",
+ "SessionE2EESecrets",
+ "SessionMatrix",
+ "SessionVerification",
+ "SessionVerificationError",
+ "SessionVerificationOtherDevice",
+ "SessionVerificationQr",
+ "SessionVerificationSAS",
+]
+
+
+class SessionE2EESecrets(BaseModel):
+ """Encrypted messaging keys available on this device."""
+
+ master_key: bool = FieldInfo(alias="masterKey")
+ """Whether the account identity key is available."""
+
+ megolm_backup_key: bool = FieldInfo(alias="megolmBackupKey")
+ """Whether the encrypted message backup key is available."""
+
+ recovery_key: bool = FieldInfo(alias="recoveryKey")
+ """Whether a recovery key is available."""
+
+ self_signing_key: bool = FieldInfo(alias="selfSigningKey")
+ """Whether the device trust key is available."""
+
+ user_signing_key: bool = FieldInfo(alias="userSigningKey")
+ """Whether the user trust key is available."""
+
+
+class SessionE2EE(BaseModel):
+ """Encrypted messaging setup status."""
+
+ cross_signing: bool = FieldInfo(alias="crossSigning")
+ """Whether this account can verify trusted devices."""
+
+ first_sync_done: bool = FieldInfo(alias="firstSyncDone")
+ """Whether the first encrypted message sync is complete."""
+
+ has_backed_up_recovery_key: bool = FieldInfo(alias="hasBackedUpRecoveryKey")
+ """Whether the user confirmed that they saved their recovery key."""
+
+ initialized: bool
+ """Whether encrypted messaging setup has started."""
+
+ key_backup: bool = FieldInfo(alias="keyBackup")
+ """Whether encrypted message backup is available."""
+
+ secrets: SessionE2EESecrets
+ """Encrypted messaging keys available on this device."""
+
+ secret_storage: bool = FieldInfo(alias="secretStorage")
+ """Whether secure key storage is available."""
+
+ verified: bool
+ """Whether this device is trusted for encrypted messages."""
+
+ recovery_key_generated_at: Optional[float] = FieldInfo(alias="recoveryKeyGeneratedAt", default=None)
+ """Unix timestamp for when the recovery key was created."""
+
+
+class SessionMatrix(BaseModel):
+ """Signed-in account details. Omitted until sign-in is complete."""
+
+ device_id: str = FieldInfo(alias="deviceID")
+ """Current device ID."""
+
+ homeserver: str
+ """Beeper homeserver URL for this account."""
+
+ user_id: str = FieldInfo(alias="userID")
+ """Signed-in Beeper user ID."""
+
+
+class SessionVerificationError(BaseModel):
+ """Verification error details, if verification stopped."""
+
+ code: str
+ """Verification error code."""
+
+ reason: str
+ """User-facing verification error message."""
+
+
+class SessionVerificationOtherDevice(BaseModel):
+ """Other device participating in verification."""
+
+ id: str
+ """Other device ID."""
+
+ name: Optional[str] = None
+ """Other device display name, if known."""
+
+
+class SessionVerificationQr(BaseModel):
+ """QR verification data."""
+
+ data: str
+ """QR code payload to display for verification."""
+
+
+class SessionVerificationSAS(BaseModel):
+ """Emoji or number comparison data for verification."""
+
+ emojis: str
+ """Emoji sequence to compare on both devices."""
+
+ decimals: Optional[str] = None
+ """Number sequence to compare on both devices."""
+
+
+class SessionVerification(BaseModel):
+ """Trusted device verification progress."""
+
+ id: str
+ """Verification ID to pass in verification action paths."""
+
+ available_actions: List[Literal["accept", "cancel", "qr.confirmScanned", "sas.start", "sas.confirm"]] = FieldInfo(
+ alias="availableActions"
+ )
+ """Verification actions that are valid for the current state."""
+
+ direction: Literal["incoming", "outgoing"]
+ """Whether this device started or received the verification."""
+
+ methods: List[Literal["qr", "sas"]]
+ """Verification methods supported for this transaction."""
+
+ purpose: Literal["login", "device"]
+ """Why this verification exists."""
+
+ state: Literal["requested", "ready", "sas_ready", "qr_scanned", "done", "cancelled", "error"]
+ """Current trusted-device verification state."""
+
+ error: Optional[SessionVerificationError] = None
+ """Verification error details, if verification stopped."""
+
+ other_device: Optional[SessionVerificationOtherDevice] = FieldInfo(alias="otherDevice", default=None)
+ """Other device participating in verification."""
+
+ other_user_id: Optional[str] = FieldInfo(alias="otherUserID", default=None)
+ """Other Beeper user participating in verification."""
+
+ qr: Optional[SessionVerificationQr] = None
+ """QR verification data."""
+
+ sas: Optional[SessionVerificationSAS] = None
+ """Emoji or number comparison data for verification."""
+
+
+class Session(BaseModel):
+ """
+ Current app sign-in and encrypted messaging setup state after creating the new recovery key.
+ """
+
+ e2ee: SessionE2EE
+ """Encrypted messaging setup status."""
+
+ state: Literal[
+ "needs-login",
+ "initializing",
+ "needs-cross-signing-setup",
+ "needs-verification",
+ "needs-secrets",
+ "needs-first-sync",
+ "ready",
+ ]
+ """
+ Current sign-in and encrypted messaging setup state for Beeper Desktop or Beeper
+ Server.
+ """
+
+ matrix: Optional[SessionMatrix] = None
+ """Signed-in account details. Omitted until sign-in is complete."""
+
+ verification: Optional[SessionVerification] = None
+ """Trusted device verification progress."""
+
+
+class ResetCreateResponse(BaseModel):
+ recovery_key: str = FieldInfo(alias="recoveryKey")
+ """New recovery key. Show it once and ask the user to save it."""
+
+ session: Session
+ """
+ Current app sign-in and encrypted messaging setup state after creating the new
+ recovery key.
+ """
diff --git a/src/beeper_desktop_api/types/app/login/verification/recovery_key_verify_params.py b/src/beeper_desktop_api/types/app/login/verification/recovery_key_verify_params.py
new file mode 100644
index 0000000..cdb0e08
--- /dev/null
+++ b/src/beeper_desktop_api/types/app/login/verification/recovery_key_verify_params.py
@@ -0,0 +1,14 @@
+# File generated from our OpenAPI spec by Stainless. See CONTRIBUTING.md for details.
+
+from __future__ import annotations
+
+from typing_extensions import Required, Annotated, TypedDict
+
+from ....._utils import PropertyInfo
+
+__all__ = ["RecoveryKeyVerifyParams"]
+
+
+class RecoveryKeyVerifyParams(TypedDict, total=False):
+ recovery_key: Required[Annotated[str, PropertyInfo(alias="recoveryKey")]]
+ """Recovery key saved by the user."""
diff --git a/src/beeper_desktop_api/types/app/login/verification/recovery_key_verify_response.py b/src/beeper_desktop_api/types/app/login/verification/recovery_key_verify_response.py
new file mode 100644
index 0000000..1c3baaa
--- /dev/null
+++ b/src/beeper_desktop_api/types/app/login/verification/recovery_key_verify_response.py
@@ -0,0 +1,192 @@
+# File generated from our OpenAPI spec by Stainless. See CONTRIBUTING.md for details.
+
+from typing import List, Optional
+from typing_extensions import Literal
+
+from pydantic import Field as FieldInfo
+
+from ....._models import BaseModel
+
+__all__ = [
+ "RecoveryKeyVerifyResponse",
+ "Session",
+ "SessionE2EE",
+ "SessionE2EESecrets",
+ "SessionMatrix",
+ "SessionVerification",
+ "SessionVerificationError",
+ "SessionVerificationOtherDevice",
+ "SessionVerificationQr",
+ "SessionVerificationSAS",
+]
+
+
+class SessionE2EESecrets(BaseModel):
+ """Encrypted messaging keys available on this device."""
+
+ master_key: bool = FieldInfo(alias="masterKey")
+ """Whether the account identity key is available."""
+
+ megolm_backup_key: bool = FieldInfo(alias="megolmBackupKey")
+ """Whether the encrypted message backup key is available."""
+
+ recovery_key: bool = FieldInfo(alias="recoveryKey")
+ """Whether a recovery key is available."""
+
+ self_signing_key: bool = FieldInfo(alias="selfSigningKey")
+ """Whether the device trust key is available."""
+
+ user_signing_key: bool = FieldInfo(alias="userSigningKey")
+ """Whether the user trust key is available."""
+
+
+class SessionE2EE(BaseModel):
+ """Encrypted messaging setup status."""
+
+ cross_signing: bool = FieldInfo(alias="crossSigning")
+ """Whether this account can verify trusted devices."""
+
+ first_sync_done: bool = FieldInfo(alias="firstSyncDone")
+ """Whether the first encrypted message sync is complete."""
+
+ has_backed_up_recovery_key: bool = FieldInfo(alias="hasBackedUpRecoveryKey")
+ """Whether the user confirmed that they saved their recovery key."""
+
+ initialized: bool
+ """Whether encrypted messaging setup has started."""
+
+ key_backup: bool = FieldInfo(alias="keyBackup")
+ """Whether encrypted message backup is available."""
+
+ secrets: SessionE2EESecrets
+ """Encrypted messaging keys available on this device."""
+
+ secret_storage: bool = FieldInfo(alias="secretStorage")
+ """Whether secure key storage is available."""
+
+ verified: bool
+ """Whether this device is trusted for encrypted messages."""
+
+ recovery_key_generated_at: Optional[float] = FieldInfo(alias="recoveryKeyGeneratedAt", default=None)
+ """Unix timestamp for when the recovery key was created."""
+
+
+class SessionMatrix(BaseModel):
+ """Signed-in account details. Omitted until sign-in is complete."""
+
+ device_id: str = FieldInfo(alias="deviceID")
+ """Current device ID."""
+
+ homeserver: str
+ """Beeper homeserver URL for this account."""
+
+ user_id: str = FieldInfo(alias="userID")
+ """Signed-in Beeper user ID."""
+
+
+class SessionVerificationError(BaseModel):
+ """Verification error details, if verification stopped."""
+
+ code: str
+ """Verification error code."""
+
+ reason: str
+ """User-facing verification error message."""
+
+
+class SessionVerificationOtherDevice(BaseModel):
+ """Other device participating in verification."""
+
+ id: str
+ """Other device ID."""
+
+ name: Optional[str] = None
+ """Other device display name, if known."""
+
+
+class SessionVerificationQr(BaseModel):
+ """QR verification data."""
+
+ data: str
+ """QR code payload to display for verification."""
+
+
+class SessionVerificationSAS(BaseModel):
+ """Emoji or number comparison data for verification."""
+
+ emojis: str
+ """Emoji sequence to compare on both devices."""
+
+ decimals: Optional[str] = None
+ """Number sequence to compare on both devices."""
+
+
+class SessionVerification(BaseModel):
+ """Trusted device verification progress."""
+
+ id: str
+ """Verification ID to pass in verification action paths."""
+
+ available_actions: List[Literal["accept", "cancel", "qr.confirmScanned", "sas.start", "sas.confirm"]] = FieldInfo(
+ alias="availableActions"
+ )
+ """Verification actions that are valid for the current state."""
+
+ direction: Literal["incoming", "outgoing"]
+ """Whether this device started or received the verification."""
+
+ methods: List[Literal["qr", "sas"]]
+ """Verification methods supported for this transaction."""
+
+ purpose: Literal["login", "device"]
+ """Why this verification exists."""
+
+ state: Literal["requested", "ready", "sas_ready", "qr_scanned", "done", "cancelled", "error"]
+ """Current trusted-device verification state."""
+
+ error: Optional[SessionVerificationError] = None
+ """Verification error details, if verification stopped."""
+
+ other_device: Optional[SessionVerificationOtherDevice] = FieldInfo(alias="otherDevice", default=None)
+ """Other device participating in verification."""
+
+ other_user_id: Optional[str] = FieldInfo(alias="otherUserID", default=None)
+ """Other Beeper user participating in verification."""
+
+ qr: Optional[SessionVerificationQr] = None
+ """QR verification data."""
+
+ sas: Optional[SessionVerificationSAS] = None
+ """Emoji or number comparison data for verification."""
+
+
+class Session(BaseModel):
+ """Current app sign-in and encrypted messaging setup state."""
+
+ e2ee: SessionE2EE
+ """Encrypted messaging setup status."""
+
+ state: Literal[
+ "needs-login",
+ "initializing",
+ "needs-cross-signing-setup",
+ "needs-verification",
+ "needs-secrets",
+ "needs-first-sync",
+ "ready",
+ ]
+ """
+ Current sign-in and encrypted messaging setup state for Beeper Desktop or Beeper
+ Server.
+ """
+
+ matrix: Optional[SessionMatrix] = None
+ """Signed-in account details. Omitted until sign-in is complete."""
+
+ verification: Optional[SessionVerification] = None
+ """Trusted device verification progress."""
+
+
+class RecoveryKeyVerifyResponse(BaseModel):
+ session: Session
+ """Current app sign-in and encrypted messaging setup state."""
diff --git a/src/beeper_desktop_api/types/app/login_email_params.py b/src/beeper_desktop_api/types/app/login_email_params.py
new file mode 100644
index 0000000..30062dd
--- /dev/null
+++ b/src/beeper_desktop_api/types/app/login_email_params.py
@@ -0,0 +1,17 @@
+# File generated from our OpenAPI spec by Stainless. See CONTRIBUTING.md for details.
+
+from __future__ import annotations
+
+from typing_extensions import Required, Annotated, TypedDict
+
+from ..._utils import PropertyInfo
+
+__all__ = ["LoginEmailParams"]
+
+
+class LoginEmailParams(TypedDict, total=False):
+ email: Required[str]
+ """Email address to send the sign-in code to."""
+
+ setup_request_id: Required[Annotated[str, PropertyInfo(alias="setupRequestID")]]
+ """Setup request ID returned by the start step."""
diff --git a/src/beeper_desktop_api/types/app/login_register_params.py b/src/beeper_desktop_api/types/app/login_register_params.py
new file mode 100644
index 0000000..7848626
--- /dev/null
+++ b/src/beeper_desktop_api/types/app/login_register_params.py
@@ -0,0 +1,27 @@
+# File generated from our OpenAPI spec by Stainless. See CONTRIBUTING.md for details.
+
+from __future__ import annotations
+
+from typing_extensions import Literal, Required, Annotated, TypedDict
+
+from ..._utils import PropertyInfo
+
+__all__ = ["LoginRegisterParams"]
+
+
+class LoginRegisterParams(TypedDict, total=False):
+ accept_terms: Required[Annotated[Literal[True], PropertyInfo(alias="acceptTerms")]]
+ """
+ Confirms that the user agreed to our
+ [terms of use](https://www.beeper.com/terms-onboarding) and has read our
+ [privacy policy](https://www.beeper.com/privacy).
+ """
+
+ lead_token: Required[Annotated[str, PropertyInfo(alias="leadToken")]]
+ """Registration token returned by Beeper."""
+
+ setup_request_id: Required[Annotated[str, PropertyInfo(alias="setupRequestID")]]
+ """Setup request ID returned by the start step."""
+
+ username: Required[str]
+ """Username selected by the user."""
diff --git a/src/beeper_desktop_api/types/app/login_register_response.py b/src/beeper_desktop_api/types/app/login_register_response.py
new file mode 100644
index 0000000..c67f552
--- /dev/null
+++ b/src/beeper_desktop_api/types/app/login_register_response.py
@@ -0,0 +1,212 @@
+# File generated from our OpenAPI spec by Stainless. See CONTRIBUTING.md for details.
+
+from typing import List, Optional
+from typing_extensions import Literal
+
+from pydantic import Field as FieldInfo
+
+from ..._models import BaseModel
+
+__all__ = [
+ "LoginRegisterResponse",
+ "Matrix",
+ "Session",
+ "SessionE2EE",
+ "SessionE2EESecrets",
+ "SessionMatrix",
+ "SessionVerification",
+ "SessionVerificationError",
+ "SessionVerificationOtherDevice",
+ "SessionVerificationQr",
+ "SessionVerificationSAS",
+]
+
+
+class Matrix(BaseModel):
+ """Account credentials for first-party app setup."""
+
+ access_token: str = FieldInfo(alias="accessToken")
+ """Beeper account access token. Returned once for first-party app setup."""
+
+ device_id: str = FieldInfo(alias="deviceID")
+ """Current device ID."""
+
+ homeserver: str
+ """Beeper homeserver URL for this account."""
+
+ user_id: str = FieldInfo(alias="userID")
+ """Signed-in Beeper user ID."""
+
+
+class SessionE2EESecrets(BaseModel):
+ """Encrypted messaging keys available on this device."""
+
+ master_key: bool = FieldInfo(alias="masterKey")
+ """Whether the account identity key is available."""
+
+ megolm_backup_key: bool = FieldInfo(alias="megolmBackupKey")
+ """Whether the encrypted message backup key is available."""
+
+ recovery_key: bool = FieldInfo(alias="recoveryKey")
+ """Whether a recovery key is available."""
+
+ self_signing_key: bool = FieldInfo(alias="selfSigningKey")
+ """Whether the device trust key is available."""
+
+ user_signing_key: bool = FieldInfo(alias="userSigningKey")
+ """Whether the user trust key is available."""
+
+
+class SessionE2EE(BaseModel):
+ """Encrypted messaging setup status."""
+
+ cross_signing: bool = FieldInfo(alias="crossSigning")
+ """Whether this account can verify trusted devices."""
+
+ first_sync_done: bool = FieldInfo(alias="firstSyncDone")
+ """Whether the first encrypted message sync is complete."""
+
+ has_backed_up_recovery_key: bool = FieldInfo(alias="hasBackedUpRecoveryKey")
+ """Whether the user confirmed that they saved their recovery key."""
+
+ initialized: bool
+ """Whether encrypted messaging setup has started."""
+
+ key_backup: bool = FieldInfo(alias="keyBackup")
+ """Whether encrypted message backup is available."""
+
+ secrets: SessionE2EESecrets
+ """Encrypted messaging keys available on this device."""
+
+ secret_storage: bool = FieldInfo(alias="secretStorage")
+ """Whether secure key storage is available."""
+
+ verified: bool
+ """Whether this device is trusted for encrypted messages."""
+
+ recovery_key_generated_at: Optional[float] = FieldInfo(alias="recoveryKeyGeneratedAt", default=None)
+ """Unix timestamp for when the recovery key was created."""
+
+
+class SessionMatrix(BaseModel):
+ """Signed-in account details. Omitted until sign-in is complete."""
+
+ device_id: str = FieldInfo(alias="deviceID")
+ """Current device ID."""
+
+ homeserver: str
+ """Beeper homeserver URL for this account."""
+
+ user_id: str = FieldInfo(alias="userID")
+ """Signed-in Beeper user ID."""
+
+
+class SessionVerificationError(BaseModel):
+ """Verification error details, if verification stopped."""
+
+ code: str
+ """Verification error code."""
+
+ reason: str
+ """User-facing verification error message."""
+
+
+class SessionVerificationOtherDevice(BaseModel):
+ """Other device participating in verification."""
+
+ id: str
+ """Other device ID."""
+
+ name: Optional[str] = None
+ """Other device display name, if known."""
+
+
+class SessionVerificationQr(BaseModel):
+ """QR verification data."""
+
+ data: str
+ """QR code payload to display for verification."""
+
+
+class SessionVerificationSAS(BaseModel):
+ """Emoji or number comparison data for verification."""
+
+ emojis: str
+ """Emoji sequence to compare on both devices."""
+
+ decimals: Optional[str] = None
+ """Number sequence to compare on both devices."""
+
+
+class SessionVerification(BaseModel):
+ """Trusted device verification progress."""
+
+ id: str
+ """Verification ID to pass in verification action paths."""
+
+ available_actions: List[Literal["accept", "cancel", "qr.confirmScanned", "sas.start", "sas.confirm"]] = FieldInfo(
+ alias="availableActions"
+ )
+ """Verification actions that are valid for the current state."""
+
+ direction: Literal["incoming", "outgoing"]
+ """Whether this device started or received the verification."""
+
+ methods: List[Literal["qr", "sas"]]
+ """Verification methods supported for this transaction."""
+
+ purpose: Literal["login", "device"]
+ """Why this verification exists."""
+
+ state: Literal["requested", "ready", "sas_ready", "qr_scanned", "done", "cancelled", "error"]
+ """Current trusted-device verification state."""
+
+ error: Optional[SessionVerificationError] = None
+ """Verification error details, if verification stopped."""
+
+ other_device: Optional[SessionVerificationOtherDevice] = FieldInfo(alias="otherDevice", default=None)
+ """Other device participating in verification."""
+
+ other_user_id: Optional[str] = FieldInfo(alias="otherUserID", default=None)
+ """Other Beeper user participating in verification."""
+
+ qr: Optional[SessionVerificationQr] = None
+ """QR verification data."""
+
+ sas: Optional[SessionVerificationSAS] = None
+ """Emoji or number comparison data for verification."""
+
+
+class Session(BaseModel):
+ """Current app sign-in and encrypted messaging setup state after sign-in."""
+
+ e2ee: SessionE2EE
+ """Encrypted messaging setup status."""
+
+ state: Literal[
+ "needs-login",
+ "initializing",
+ "needs-cross-signing-setup",
+ "needs-verification",
+ "needs-secrets",
+ "needs-first-sync",
+ "ready",
+ ]
+ """
+ Current sign-in and encrypted messaging setup state for Beeper Desktop or Beeper
+ Server.
+ """
+
+ matrix: Optional[SessionMatrix] = None
+ """Signed-in account details. Omitted until sign-in is complete."""
+
+ verification: Optional[SessionVerification] = None
+ """Trusted device verification progress."""
+
+
+class LoginRegisterResponse(BaseModel):
+ matrix: Matrix
+ """Account credentials for first-party app setup."""
+
+ session: Session
+ """Current app sign-in and encrypted messaging setup state after sign-in."""
diff --git a/src/beeper_desktop_api/types/app/login_response_params.py b/src/beeper_desktop_api/types/app/login_response_params.py
new file mode 100644
index 0000000..72a2b3a
--- /dev/null
+++ b/src/beeper_desktop_api/types/app/login_response_params.py
@@ -0,0 +1,17 @@
+# File generated from our OpenAPI spec by Stainless. See CONTRIBUTING.md for details.
+
+from __future__ import annotations
+
+from typing_extensions import Required, Annotated, TypedDict
+
+from ..._utils import PropertyInfo
+
+__all__ = ["LoginResponseParams"]
+
+
+class LoginResponseParams(TypedDict, total=False):
+ response: Required[str]
+ """Sign-in code from the user email."""
+
+ setup_request_id: Required[Annotated[str, PropertyInfo(alias="setupRequestID")]]
+ """Setup request ID returned by the start step."""
diff --git a/src/beeper_desktop_api/types/app/login_response_response.py b/src/beeper_desktop_api/types/app/login_response_response.py
new file mode 100644
index 0000000..fb75746
--- /dev/null
+++ b/src/beeper_desktop_api/types/app/login_response_response.py
@@ -0,0 +1,251 @@
+# File generated from our OpenAPI spec by Stainless. See CONTRIBUTING.md for details.
+
+from typing import List, Union, Optional
+from typing_extensions import Literal, TypeAlias
+
+from pydantic import Field as FieldInfo
+
+from ..._models import BaseModel
+
+__all__ = [
+ "LoginResponseResponse",
+ "Success",
+ "SuccessMatrix",
+ "SuccessSession",
+ "SuccessSessionE2EE",
+ "SuccessSessionE2EESecrets",
+ "SuccessSessionMatrix",
+ "SuccessSessionVerification",
+ "SuccessSessionVerificationError",
+ "SuccessSessionVerificationOtherDevice",
+ "SuccessSessionVerificationQr",
+ "SuccessSessionVerificationSAS",
+ "RegistrationRequired",
+ "RegistrationRequiredCopy",
+]
+
+
+class SuccessMatrix(BaseModel):
+ """Account credentials for first-party app setup."""
+
+ access_token: str = FieldInfo(alias="accessToken")
+ """Beeper account access token. Returned once for first-party app setup."""
+
+ device_id: str = FieldInfo(alias="deviceID")
+ """Current device ID."""
+
+ homeserver: str
+ """Beeper homeserver URL for this account."""
+
+ user_id: str = FieldInfo(alias="userID")
+ """Signed-in Beeper user ID."""
+
+
+class SuccessSessionE2EESecrets(BaseModel):
+ """Encrypted messaging keys available on this device."""
+
+ master_key: bool = FieldInfo(alias="masterKey")
+ """Whether the account identity key is available."""
+
+ megolm_backup_key: bool = FieldInfo(alias="megolmBackupKey")
+ """Whether the encrypted message backup key is available."""
+
+ recovery_key: bool = FieldInfo(alias="recoveryKey")
+ """Whether a recovery key is available."""
+
+ self_signing_key: bool = FieldInfo(alias="selfSigningKey")
+ """Whether the device trust key is available."""
+
+ user_signing_key: bool = FieldInfo(alias="userSigningKey")
+ """Whether the user trust key is available."""
+
+
+class SuccessSessionE2EE(BaseModel):
+ """Encrypted messaging setup status."""
+
+ cross_signing: bool = FieldInfo(alias="crossSigning")
+ """Whether this account can verify trusted devices."""
+
+ first_sync_done: bool = FieldInfo(alias="firstSyncDone")
+ """Whether the first encrypted message sync is complete."""
+
+ has_backed_up_recovery_key: bool = FieldInfo(alias="hasBackedUpRecoveryKey")
+ """Whether the user confirmed that they saved their recovery key."""
+
+ initialized: bool
+ """Whether encrypted messaging setup has started."""
+
+ key_backup: bool = FieldInfo(alias="keyBackup")
+ """Whether encrypted message backup is available."""
+
+ secrets: SuccessSessionE2EESecrets
+ """Encrypted messaging keys available on this device."""
+
+ secret_storage: bool = FieldInfo(alias="secretStorage")
+ """Whether secure key storage is available."""
+
+ verified: bool
+ """Whether this device is trusted for encrypted messages."""
+
+ recovery_key_generated_at: Optional[float] = FieldInfo(alias="recoveryKeyGeneratedAt", default=None)
+ """Unix timestamp for when the recovery key was created."""
+
+
+class SuccessSessionMatrix(BaseModel):
+ """Signed-in account details. Omitted until sign-in is complete."""
+
+ device_id: str = FieldInfo(alias="deviceID")
+ """Current device ID."""
+
+ homeserver: str
+ """Beeper homeserver URL for this account."""
+
+ user_id: str = FieldInfo(alias="userID")
+ """Signed-in Beeper user ID."""
+
+
+class SuccessSessionVerificationError(BaseModel):
+ """Verification error details, if verification stopped."""
+
+ code: str
+ """Verification error code."""
+
+ reason: str
+ """User-facing verification error message."""
+
+
+class SuccessSessionVerificationOtherDevice(BaseModel):
+ """Other device participating in verification."""
+
+ id: str
+ """Other device ID."""
+
+ name: Optional[str] = None
+ """Other device display name, if known."""
+
+
+class SuccessSessionVerificationQr(BaseModel):
+ """QR verification data."""
+
+ data: str
+ """QR code payload to display for verification."""
+
+
+class SuccessSessionVerificationSAS(BaseModel):
+ """Emoji or number comparison data for verification."""
+
+ emojis: str
+ """Emoji sequence to compare on both devices."""
+
+ decimals: Optional[str] = None
+ """Number sequence to compare on both devices."""
+
+
+class SuccessSessionVerification(BaseModel):
+ """Trusted device verification progress."""
+
+ id: str
+ """Verification ID to pass in verification action paths."""
+
+ available_actions: List[Literal["accept", "cancel", "qr.confirmScanned", "sas.start", "sas.confirm"]] = FieldInfo(
+ alias="availableActions"
+ )
+ """Verification actions that are valid for the current state."""
+
+ direction: Literal["incoming", "outgoing"]
+ """Whether this device started or received the verification."""
+
+ methods: List[Literal["qr", "sas"]]
+ """Verification methods supported for this transaction."""
+
+ purpose: Literal["login", "device"]
+ """Why this verification exists."""
+
+ state: Literal["requested", "ready", "sas_ready", "qr_scanned", "done", "cancelled", "error"]
+ """Current trusted-device verification state."""
+
+ error: Optional[SuccessSessionVerificationError] = None
+ """Verification error details, if verification stopped."""
+
+ other_device: Optional[SuccessSessionVerificationOtherDevice] = FieldInfo(alias="otherDevice", default=None)
+ """Other device participating in verification."""
+
+ other_user_id: Optional[str] = FieldInfo(alias="otherUserID", default=None)
+ """Other Beeper user participating in verification."""
+
+ qr: Optional[SuccessSessionVerificationQr] = None
+ """QR verification data."""
+
+ sas: Optional[SuccessSessionVerificationSAS] = None
+ """Emoji or number comparison data for verification."""
+
+
+class SuccessSession(BaseModel):
+ """Current app sign-in and encrypted messaging setup state after sign-in."""
+
+ e2ee: SuccessSessionE2EE
+ """Encrypted messaging setup status."""
+
+ state: Literal[
+ "needs-login",
+ "initializing",
+ "needs-cross-signing-setup",
+ "needs-verification",
+ "needs-secrets",
+ "needs-first-sync",
+ "ready",
+ ]
+ """
+ Current sign-in and encrypted messaging setup state for Beeper Desktop or Beeper
+ Server.
+ """
+
+ matrix: Optional[SuccessSessionMatrix] = None
+ """Signed-in account details. Omitted until sign-in is complete."""
+
+ verification: Optional[SuccessSessionVerification] = None
+ """Trusted device verification progress."""
+
+
+class Success(BaseModel):
+ matrix: SuccessMatrix
+ """Account credentials for first-party app setup."""
+
+ session: SuccessSession
+ """Current app sign-in and encrypted messaging setup state after sign-in."""
+
+
+class RegistrationRequiredCopy(BaseModel):
+ """Copy to display during account creation."""
+
+ submit: Literal["Continue"]
+ """Submit button label."""
+
+ terms: Literal["By continuing, you agree to the Terms of Use and acknowledge the Privacy Policy."]
+ """Terms and privacy notice to show before account creation."""
+
+ title: Literal["Choose your username"]
+ """Title for the username step."""
+
+ username_placeholder: Literal["Username"] = FieldInfo(alias="usernamePlaceholder")
+ """Placeholder for the username field."""
+
+
+class RegistrationRequired(BaseModel):
+ copy_: RegistrationRequiredCopy = FieldInfo(alias="copy")
+ """Copy to display during account creation."""
+
+ lead_token: str = FieldInfo(alias="leadToken")
+ """Registration token returned by Beeper."""
+
+ registration_required: Literal[True] = FieldInfo(alias="registrationRequired")
+ """Indicates that the user needs to create a Beeper account."""
+
+ setup_request_id: str = FieldInfo(alias="setupRequestID")
+ """Setup request ID to use when creating the account."""
+
+ username_suggestions: Optional[List[str]] = FieldInfo(alias="usernameSuggestions", default=None)
+ """Suggested usernames for the new account."""
+
+
+LoginResponseResponse: TypeAlias = Union[Success, RegistrationRequired]
diff --git a/src/beeper_desktop_api/types/app/login_start_response.py b/src/beeper_desktop_api/types/app/login_start_response.py
new file mode 100644
index 0000000..7a2d5d9
--- /dev/null
+++ b/src/beeper_desktop_api/types/app/login_start_response.py
@@ -0,0 +1,17 @@
+# File generated from our OpenAPI spec by Stainless. See CONTRIBUTING.md for details.
+
+from typing import List
+
+from pydantic import Field as FieldInfo
+
+from ..._models import BaseModel
+
+__all__ = ["LoginStartResponse"]
+
+
+class LoginStartResponse(BaseModel):
+ setup_request_id: str = FieldInfo(alias="setupRequestID")
+ """Setup request ID to use in the next sign-in step."""
+
+ sign_in_methods: List[str] = FieldInfo(alias="signInMethods")
+ """Available sign-in methods for this setup request."""
diff --git a/src/beeper_desktop_api/types/app/verification_accept_response.py b/src/beeper_desktop_api/types/app/verification_accept_response.py
new file mode 100644
index 0000000..4691a54
--- /dev/null
+++ b/src/beeper_desktop_api/types/app/verification_accept_response.py
@@ -0,0 +1,276 @@
+# File generated from our OpenAPI spec by Stainless. See CONTRIBUTING.md for details.
+
+from typing import List, Optional
+from typing_extensions import Literal
+
+from pydantic import Field as FieldInfo
+
+from ..._models import BaseModel
+
+__all__ = [
+ "VerificationAcceptResponse",
+ "Session",
+ "SessionE2EE",
+ "SessionE2EESecrets",
+ "SessionMatrix",
+ "SessionVerification",
+ "SessionVerificationError",
+ "SessionVerificationOtherDevice",
+ "SessionVerificationQr",
+ "SessionVerificationSAS",
+ "Verification",
+ "VerificationError",
+ "VerificationOtherDevice",
+ "VerificationQr",
+ "VerificationSAS",
+]
+
+
+class SessionE2EESecrets(BaseModel):
+ """Encrypted messaging keys available on this device."""
+
+ master_key: bool = FieldInfo(alias="masterKey")
+ """Whether the account identity key is available."""
+
+ megolm_backup_key: bool = FieldInfo(alias="megolmBackupKey")
+ """Whether the encrypted message backup key is available."""
+
+ recovery_key: bool = FieldInfo(alias="recoveryKey")
+ """Whether a recovery key is available."""
+
+ self_signing_key: bool = FieldInfo(alias="selfSigningKey")
+ """Whether the device trust key is available."""
+
+ user_signing_key: bool = FieldInfo(alias="userSigningKey")
+ """Whether the user trust key is available."""
+
+
+class SessionE2EE(BaseModel):
+ """Encrypted messaging setup status."""
+
+ cross_signing: bool = FieldInfo(alias="crossSigning")
+ """Whether this account can verify trusted devices."""
+
+ first_sync_done: bool = FieldInfo(alias="firstSyncDone")
+ """Whether the first encrypted message sync is complete."""
+
+ has_backed_up_recovery_key: bool = FieldInfo(alias="hasBackedUpRecoveryKey")
+ """Whether the user confirmed that they saved their recovery key."""
+
+ initialized: bool
+ """Whether encrypted messaging setup has started."""
+
+ key_backup: bool = FieldInfo(alias="keyBackup")
+ """Whether encrypted message backup is available."""
+
+ secrets: SessionE2EESecrets
+ """Encrypted messaging keys available on this device."""
+
+ secret_storage: bool = FieldInfo(alias="secretStorage")
+ """Whether secure key storage is available."""
+
+ verified: bool
+ """Whether this device is trusted for encrypted messages."""
+
+ recovery_key_generated_at: Optional[float] = FieldInfo(alias="recoveryKeyGeneratedAt", default=None)
+ """Unix timestamp for when the recovery key was created."""
+
+
+class SessionMatrix(BaseModel):
+ """Signed-in account details. Omitted until sign-in is complete."""
+
+ device_id: str = FieldInfo(alias="deviceID")
+ """Current device ID."""
+
+ homeserver: str
+ """Beeper homeserver URL for this account."""
+
+ user_id: str = FieldInfo(alias="userID")
+ """Signed-in Beeper user ID."""
+
+
+class SessionVerificationError(BaseModel):
+ """Verification error details, if verification stopped."""
+
+ code: str
+ """Verification error code."""
+
+ reason: str
+ """User-facing verification error message."""
+
+
+class SessionVerificationOtherDevice(BaseModel):
+ """Other device participating in verification."""
+
+ id: str
+ """Other device ID."""
+
+ name: Optional[str] = None
+ """Other device display name, if known."""
+
+
+class SessionVerificationQr(BaseModel):
+ """QR verification data."""
+
+ data: str
+ """QR code payload to display for verification."""
+
+
+class SessionVerificationSAS(BaseModel):
+ """Emoji or number comparison data for verification."""
+
+ emojis: str
+ """Emoji sequence to compare on both devices."""
+
+ decimals: Optional[str] = None
+ """Number sequence to compare on both devices."""
+
+
+class SessionVerification(BaseModel):
+ """Trusted device verification progress."""
+
+ id: str
+ """Verification ID to pass in verification action paths."""
+
+ available_actions: List[Literal["accept", "cancel", "qr.confirmScanned", "sas.start", "sas.confirm"]] = FieldInfo(
+ alias="availableActions"
+ )
+ """Verification actions that are valid for the current state."""
+
+ direction: Literal["incoming", "outgoing"]
+ """Whether this device started or received the verification."""
+
+ methods: List[Literal["qr", "sas"]]
+ """Verification methods supported for this transaction."""
+
+ purpose: Literal["login", "device"]
+ """Why this verification exists."""
+
+ state: Literal["requested", "ready", "sas_ready", "qr_scanned", "done", "cancelled", "error"]
+ """Current trusted-device verification state."""
+
+ error: Optional[SessionVerificationError] = None
+ """Verification error details, if verification stopped."""
+
+ other_device: Optional[SessionVerificationOtherDevice] = FieldInfo(alias="otherDevice", default=None)
+ """Other device participating in verification."""
+
+ other_user_id: Optional[str] = FieldInfo(alias="otherUserID", default=None)
+ """Other Beeper user participating in verification."""
+
+ qr: Optional[SessionVerificationQr] = None
+ """QR verification data."""
+
+ sas: Optional[SessionVerificationSAS] = None
+ """Emoji or number comparison data for verification."""
+
+
+class Session(BaseModel):
+ """Current app sign-in and encrypted messaging setup state."""
+
+ e2ee: SessionE2EE
+ """Encrypted messaging setup status."""
+
+ state: Literal[
+ "needs-login",
+ "initializing",
+ "needs-cross-signing-setup",
+ "needs-verification",
+ "needs-secrets",
+ "needs-first-sync",
+ "ready",
+ ]
+ """
+ Current sign-in and encrypted messaging setup state for Beeper Desktop or Beeper
+ Server.
+ """
+
+ matrix: Optional[SessionMatrix] = None
+ """Signed-in account details. Omitted until sign-in is complete."""
+
+ verification: Optional[SessionVerification] = None
+ """Trusted device verification progress."""
+
+
+class VerificationError(BaseModel):
+ """Verification error details, if verification stopped."""
+
+ code: str
+ """Verification error code."""
+
+ reason: str
+ """User-facing verification error message."""
+
+
+class VerificationOtherDevice(BaseModel):
+ """Other device participating in verification."""
+
+ id: str
+ """Other device ID."""
+
+ name: Optional[str] = None
+ """Other device display name, if known."""
+
+
+class VerificationQr(BaseModel):
+ """QR verification data."""
+
+ data: str
+ """QR code payload to display for verification."""
+
+
+class VerificationSAS(BaseModel):
+ """Emoji or number comparison data for verification."""
+
+ emojis: str
+ """Emoji sequence to compare on both devices."""
+
+ decimals: Optional[str] = None
+ """Number sequence to compare on both devices."""
+
+
+class Verification(BaseModel):
+ """Trusted device verification progress."""
+
+ id: str
+ """Verification ID to pass in verification action paths."""
+
+ available_actions: List[Literal["accept", "cancel", "qr.confirmScanned", "sas.start", "sas.confirm"]] = FieldInfo(
+ alias="availableActions"
+ )
+ """Verification actions that are valid for the current state."""
+
+ direction: Literal["incoming", "outgoing"]
+ """Whether this device started or received the verification."""
+
+ methods: List[Literal["qr", "sas"]]
+ """Verification methods supported for this transaction."""
+
+ purpose: Literal["login", "device"]
+ """Why this verification exists."""
+
+ state: Literal["requested", "ready", "sas_ready", "qr_scanned", "done", "cancelled", "error"]
+ """Current trusted-device verification state."""
+
+ error: Optional[VerificationError] = None
+ """Verification error details, if verification stopped."""
+
+ other_device: Optional[VerificationOtherDevice] = FieldInfo(alias="otherDevice", default=None)
+ """Other device participating in verification."""
+
+ other_user_id: Optional[str] = FieldInfo(alias="otherUserID", default=None)
+ """Other Beeper user participating in verification."""
+
+ qr: Optional[VerificationQr] = None
+ """QR verification data."""
+
+ sas: Optional[VerificationSAS] = None
+ """Emoji or number comparison data for verification."""
+
+
+class VerificationAcceptResponse(BaseModel):
+ session: Session
+ """Current app sign-in and encrypted messaging setup state."""
+
+ verification: Optional[Verification] = None
+ """Trusted device verification progress."""
diff --git a/src/beeper_desktop_api/types/app/verification_cancel_params.py b/src/beeper_desktop_api/types/app/verification_cancel_params.py
new file mode 100644
index 0000000..d954b97
--- /dev/null
+++ b/src/beeper_desktop_api/types/app/verification_cancel_params.py
@@ -0,0 +1,15 @@
+# File generated from our OpenAPI spec by Stainless. See CONTRIBUTING.md for details.
+
+from __future__ import annotations
+
+from typing_extensions import TypedDict
+
+__all__ = ["VerificationCancelParams"]
+
+
+class VerificationCancelParams(TypedDict, total=False):
+ code: str
+ """Optional cancellation code."""
+
+ reason: str
+ """Optional user-facing cancellation reason."""
diff --git a/src/beeper_desktop_api/types/app/verification_cancel_response.py b/src/beeper_desktop_api/types/app/verification_cancel_response.py
new file mode 100644
index 0000000..68dfc2f
--- /dev/null
+++ b/src/beeper_desktop_api/types/app/verification_cancel_response.py
@@ -0,0 +1,276 @@
+# File generated from our OpenAPI spec by Stainless. See CONTRIBUTING.md for details.
+
+from typing import List, Optional
+from typing_extensions import Literal
+
+from pydantic import Field as FieldInfo
+
+from ..._models import BaseModel
+
+__all__ = [
+ "VerificationCancelResponse",
+ "Session",
+ "SessionE2EE",
+ "SessionE2EESecrets",
+ "SessionMatrix",
+ "SessionVerification",
+ "SessionVerificationError",
+ "SessionVerificationOtherDevice",
+ "SessionVerificationQr",
+ "SessionVerificationSAS",
+ "Verification",
+ "VerificationError",
+ "VerificationOtherDevice",
+ "VerificationQr",
+ "VerificationSAS",
+]
+
+
+class SessionE2EESecrets(BaseModel):
+ """Encrypted messaging keys available on this device."""
+
+ master_key: bool = FieldInfo(alias="masterKey")
+ """Whether the account identity key is available."""
+
+ megolm_backup_key: bool = FieldInfo(alias="megolmBackupKey")
+ """Whether the encrypted message backup key is available."""
+
+ recovery_key: bool = FieldInfo(alias="recoveryKey")
+ """Whether a recovery key is available."""
+
+ self_signing_key: bool = FieldInfo(alias="selfSigningKey")
+ """Whether the device trust key is available."""
+
+ user_signing_key: bool = FieldInfo(alias="userSigningKey")
+ """Whether the user trust key is available."""
+
+
+class SessionE2EE(BaseModel):
+ """Encrypted messaging setup status."""
+
+ cross_signing: bool = FieldInfo(alias="crossSigning")
+ """Whether this account can verify trusted devices."""
+
+ first_sync_done: bool = FieldInfo(alias="firstSyncDone")
+ """Whether the first encrypted message sync is complete."""
+
+ has_backed_up_recovery_key: bool = FieldInfo(alias="hasBackedUpRecoveryKey")
+ """Whether the user confirmed that they saved their recovery key."""
+
+ initialized: bool
+ """Whether encrypted messaging setup has started."""
+
+ key_backup: bool = FieldInfo(alias="keyBackup")
+ """Whether encrypted message backup is available."""
+
+ secrets: SessionE2EESecrets
+ """Encrypted messaging keys available on this device."""
+
+ secret_storage: bool = FieldInfo(alias="secretStorage")
+ """Whether secure key storage is available."""
+
+ verified: bool
+ """Whether this device is trusted for encrypted messages."""
+
+ recovery_key_generated_at: Optional[float] = FieldInfo(alias="recoveryKeyGeneratedAt", default=None)
+ """Unix timestamp for when the recovery key was created."""
+
+
+class SessionMatrix(BaseModel):
+ """Signed-in account details. Omitted until sign-in is complete."""
+
+ device_id: str = FieldInfo(alias="deviceID")
+ """Current device ID."""
+
+ homeserver: str
+ """Beeper homeserver URL for this account."""
+
+ user_id: str = FieldInfo(alias="userID")
+ """Signed-in Beeper user ID."""
+
+
+class SessionVerificationError(BaseModel):
+ """Verification error details, if verification stopped."""
+
+ code: str
+ """Verification error code."""
+
+ reason: str
+ """User-facing verification error message."""
+
+
+class SessionVerificationOtherDevice(BaseModel):
+ """Other device participating in verification."""
+
+ id: str
+ """Other device ID."""
+
+ name: Optional[str] = None
+ """Other device display name, if known."""
+
+
+class SessionVerificationQr(BaseModel):
+ """QR verification data."""
+
+ data: str
+ """QR code payload to display for verification."""
+
+
+class SessionVerificationSAS(BaseModel):
+ """Emoji or number comparison data for verification."""
+
+ emojis: str
+ """Emoji sequence to compare on both devices."""
+
+ decimals: Optional[str] = None
+ """Number sequence to compare on both devices."""
+
+
+class SessionVerification(BaseModel):
+ """Trusted device verification progress."""
+
+ id: str
+ """Verification ID to pass in verification action paths."""
+
+ available_actions: List[Literal["accept", "cancel", "qr.confirmScanned", "sas.start", "sas.confirm"]] = FieldInfo(
+ alias="availableActions"
+ )
+ """Verification actions that are valid for the current state."""
+
+ direction: Literal["incoming", "outgoing"]
+ """Whether this device started or received the verification."""
+
+ methods: List[Literal["qr", "sas"]]
+ """Verification methods supported for this transaction."""
+
+ purpose: Literal["login", "device"]
+ """Why this verification exists."""
+
+ state: Literal["requested", "ready", "sas_ready", "qr_scanned", "done", "cancelled", "error"]
+ """Current trusted-device verification state."""
+
+ error: Optional[SessionVerificationError] = None
+ """Verification error details, if verification stopped."""
+
+ other_device: Optional[SessionVerificationOtherDevice] = FieldInfo(alias="otherDevice", default=None)
+ """Other device participating in verification."""
+
+ other_user_id: Optional[str] = FieldInfo(alias="otherUserID", default=None)
+ """Other Beeper user participating in verification."""
+
+ qr: Optional[SessionVerificationQr] = None
+ """QR verification data."""
+
+ sas: Optional[SessionVerificationSAS] = None
+ """Emoji or number comparison data for verification."""
+
+
+class Session(BaseModel):
+ """Current app sign-in and encrypted messaging setup state."""
+
+ e2ee: SessionE2EE
+ """Encrypted messaging setup status."""
+
+ state: Literal[
+ "needs-login",
+ "initializing",
+ "needs-cross-signing-setup",
+ "needs-verification",
+ "needs-secrets",
+ "needs-first-sync",
+ "ready",
+ ]
+ """
+ Current sign-in and encrypted messaging setup state for Beeper Desktop or Beeper
+ Server.
+ """
+
+ matrix: Optional[SessionMatrix] = None
+ """Signed-in account details. Omitted until sign-in is complete."""
+
+ verification: Optional[SessionVerification] = None
+ """Trusted device verification progress."""
+
+
+class VerificationError(BaseModel):
+ """Verification error details, if verification stopped."""
+
+ code: str
+ """Verification error code."""
+
+ reason: str
+ """User-facing verification error message."""
+
+
+class VerificationOtherDevice(BaseModel):
+ """Other device participating in verification."""
+
+ id: str
+ """Other device ID."""
+
+ name: Optional[str] = None
+ """Other device display name, if known."""
+
+
+class VerificationQr(BaseModel):
+ """QR verification data."""
+
+ data: str
+ """QR code payload to display for verification."""
+
+
+class VerificationSAS(BaseModel):
+ """Emoji or number comparison data for verification."""
+
+ emojis: str
+ """Emoji sequence to compare on both devices."""
+
+ decimals: Optional[str] = None
+ """Number sequence to compare on both devices."""
+
+
+class Verification(BaseModel):
+ """Trusted device verification progress."""
+
+ id: str
+ """Verification ID to pass in verification action paths."""
+
+ available_actions: List[Literal["accept", "cancel", "qr.confirmScanned", "sas.start", "sas.confirm"]] = FieldInfo(
+ alias="availableActions"
+ )
+ """Verification actions that are valid for the current state."""
+
+ direction: Literal["incoming", "outgoing"]
+ """Whether this device started or received the verification."""
+
+ methods: List[Literal["qr", "sas"]]
+ """Verification methods supported for this transaction."""
+
+ purpose: Literal["login", "device"]
+ """Why this verification exists."""
+
+ state: Literal["requested", "ready", "sas_ready", "qr_scanned", "done", "cancelled", "error"]
+ """Current trusted-device verification state."""
+
+ error: Optional[VerificationError] = None
+ """Verification error details, if verification stopped."""
+
+ other_device: Optional[VerificationOtherDevice] = FieldInfo(alias="otherDevice", default=None)
+ """Other device participating in verification."""
+
+ other_user_id: Optional[str] = FieldInfo(alias="otherUserID", default=None)
+ """Other Beeper user participating in verification."""
+
+ qr: Optional[VerificationQr] = None
+ """QR verification data."""
+
+ sas: Optional[VerificationSAS] = None
+ """Emoji or number comparison data for verification."""
+
+
+class VerificationCancelResponse(BaseModel):
+ session: Session
+ """Current app sign-in and encrypted messaging setup state."""
+
+ verification: Optional[Verification] = None
+ """Trusted device verification progress."""
diff --git a/src/beeper_desktop_api/types/app/verification_create_params.py b/src/beeper_desktop_api/types/app/verification_create_params.py
new file mode 100644
index 0000000..6359e67
--- /dev/null
+++ b/src/beeper_desktop_api/types/app/verification_create_params.py
@@ -0,0 +1,17 @@
+# File generated from our OpenAPI spec by Stainless. See CONTRIBUTING.md for details.
+
+from __future__ import annotations
+
+from typing_extensions import Literal, Annotated, TypedDict
+
+from ..._utils import PropertyInfo
+
+__all__ = ["VerificationCreateParams"]
+
+
+class VerificationCreateParams(TypedDict, total=False):
+ purpose: Literal["login", "device"]
+ """Why this verification is being started."""
+
+ user_id: Annotated[str, PropertyInfo(alias="userID")]
+ """Beeper user ID to verify. Defaults to the signed-in user."""
diff --git a/src/beeper_desktop_api/types/app/verification_create_response.py b/src/beeper_desktop_api/types/app/verification_create_response.py
new file mode 100644
index 0000000..a88c143
--- /dev/null
+++ b/src/beeper_desktop_api/types/app/verification_create_response.py
@@ -0,0 +1,276 @@
+# File generated from our OpenAPI spec by Stainless. See CONTRIBUTING.md for details.
+
+from typing import List, Optional
+from typing_extensions import Literal
+
+from pydantic import Field as FieldInfo
+
+from ..._models import BaseModel
+
+__all__ = [
+ "VerificationCreateResponse",
+ "Session",
+ "SessionE2EE",
+ "SessionE2EESecrets",
+ "SessionMatrix",
+ "SessionVerification",
+ "SessionVerificationError",
+ "SessionVerificationOtherDevice",
+ "SessionVerificationQr",
+ "SessionVerificationSAS",
+ "Verification",
+ "VerificationError",
+ "VerificationOtherDevice",
+ "VerificationQr",
+ "VerificationSAS",
+]
+
+
+class SessionE2EESecrets(BaseModel):
+ """Encrypted messaging keys available on this device."""
+
+ master_key: bool = FieldInfo(alias="masterKey")
+ """Whether the account identity key is available."""
+
+ megolm_backup_key: bool = FieldInfo(alias="megolmBackupKey")
+ """Whether the encrypted message backup key is available."""
+
+ recovery_key: bool = FieldInfo(alias="recoveryKey")
+ """Whether a recovery key is available."""
+
+ self_signing_key: bool = FieldInfo(alias="selfSigningKey")
+ """Whether the device trust key is available."""
+
+ user_signing_key: bool = FieldInfo(alias="userSigningKey")
+ """Whether the user trust key is available."""
+
+
+class SessionE2EE(BaseModel):
+ """Encrypted messaging setup status."""
+
+ cross_signing: bool = FieldInfo(alias="crossSigning")
+ """Whether this account can verify trusted devices."""
+
+ first_sync_done: bool = FieldInfo(alias="firstSyncDone")
+ """Whether the first encrypted message sync is complete."""
+
+ has_backed_up_recovery_key: bool = FieldInfo(alias="hasBackedUpRecoveryKey")
+ """Whether the user confirmed that they saved their recovery key."""
+
+ initialized: bool
+ """Whether encrypted messaging setup has started."""
+
+ key_backup: bool = FieldInfo(alias="keyBackup")
+ """Whether encrypted message backup is available."""
+
+ secrets: SessionE2EESecrets
+ """Encrypted messaging keys available on this device."""
+
+ secret_storage: bool = FieldInfo(alias="secretStorage")
+ """Whether secure key storage is available."""
+
+ verified: bool
+ """Whether this device is trusted for encrypted messages."""
+
+ recovery_key_generated_at: Optional[float] = FieldInfo(alias="recoveryKeyGeneratedAt", default=None)
+ """Unix timestamp for when the recovery key was created."""
+
+
+class SessionMatrix(BaseModel):
+ """Signed-in account details. Omitted until sign-in is complete."""
+
+ device_id: str = FieldInfo(alias="deviceID")
+ """Current device ID."""
+
+ homeserver: str
+ """Beeper homeserver URL for this account."""
+
+ user_id: str = FieldInfo(alias="userID")
+ """Signed-in Beeper user ID."""
+
+
+class SessionVerificationError(BaseModel):
+ """Verification error details, if verification stopped."""
+
+ code: str
+ """Verification error code."""
+
+ reason: str
+ """User-facing verification error message."""
+
+
+class SessionVerificationOtherDevice(BaseModel):
+ """Other device participating in verification."""
+
+ id: str
+ """Other device ID."""
+
+ name: Optional[str] = None
+ """Other device display name, if known."""
+
+
+class SessionVerificationQr(BaseModel):
+ """QR verification data."""
+
+ data: str
+ """QR code payload to display for verification."""
+
+
+class SessionVerificationSAS(BaseModel):
+ """Emoji or number comparison data for verification."""
+
+ emojis: str
+ """Emoji sequence to compare on both devices."""
+
+ decimals: Optional[str] = None
+ """Number sequence to compare on both devices."""
+
+
+class SessionVerification(BaseModel):
+ """Trusted device verification progress."""
+
+ id: str
+ """Verification ID to pass in verification action paths."""
+
+ available_actions: List[Literal["accept", "cancel", "qr.confirmScanned", "sas.start", "sas.confirm"]] = FieldInfo(
+ alias="availableActions"
+ )
+ """Verification actions that are valid for the current state."""
+
+ direction: Literal["incoming", "outgoing"]
+ """Whether this device started or received the verification."""
+
+ methods: List[Literal["qr", "sas"]]
+ """Verification methods supported for this transaction."""
+
+ purpose: Literal["login", "device"]
+ """Why this verification exists."""
+
+ state: Literal["requested", "ready", "sas_ready", "qr_scanned", "done", "cancelled", "error"]
+ """Current trusted-device verification state."""
+
+ error: Optional[SessionVerificationError] = None
+ """Verification error details, if verification stopped."""
+
+ other_device: Optional[SessionVerificationOtherDevice] = FieldInfo(alias="otherDevice", default=None)
+ """Other device participating in verification."""
+
+ other_user_id: Optional[str] = FieldInfo(alias="otherUserID", default=None)
+ """Other Beeper user participating in verification."""
+
+ qr: Optional[SessionVerificationQr] = None
+ """QR verification data."""
+
+ sas: Optional[SessionVerificationSAS] = None
+ """Emoji or number comparison data for verification."""
+
+
+class Session(BaseModel):
+ """Current app sign-in and encrypted messaging setup state."""
+
+ e2ee: SessionE2EE
+ """Encrypted messaging setup status."""
+
+ state: Literal[
+ "needs-login",
+ "initializing",
+ "needs-cross-signing-setup",
+ "needs-verification",
+ "needs-secrets",
+ "needs-first-sync",
+ "ready",
+ ]
+ """
+ Current sign-in and encrypted messaging setup state for Beeper Desktop or Beeper
+ Server.
+ """
+
+ matrix: Optional[SessionMatrix] = None
+ """Signed-in account details. Omitted until sign-in is complete."""
+
+ verification: Optional[SessionVerification] = None
+ """Trusted device verification progress."""
+
+
+class VerificationError(BaseModel):
+ """Verification error details, if verification stopped."""
+
+ code: str
+ """Verification error code."""
+
+ reason: str
+ """User-facing verification error message."""
+
+
+class VerificationOtherDevice(BaseModel):
+ """Other device participating in verification."""
+
+ id: str
+ """Other device ID."""
+
+ name: Optional[str] = None
+ """Other device display name, if known."""
+
+
+class VerificationQr(BaseModel):
+ """QR verification data."""
+
+ data: str
+ """QR code payload to display for verification."""
+
+
+class VerificationSAS(BaseModel):
+ """Emoji or number comparison data for verification."""
+
+ emojis: str
+ """Emoji sequence to compare on both devices."""
+
+ decimals: Optional[str] = None
+ """Number sequence to compare on both devices."""
+
+
+class Verification(BaseModel):
+ """Trusted device verification progress."""
+
+ id: str
+ """Verification ID to pass in verification action paths."""
+
+ available_actions: List[Literal["accept", "cancel", "qr.confirmScanned", "sas.start", "sas.confirm"]] = FieldInfo(
+ alias="availableActions"
+ )
+ """Verification actions that are valid for the current state."""
+
+ direction: Literal["incoming", "outgoing"]
+ """Whether this device started or received the verification."""
+
+ methods: List[Literal["qr", "sas"]]
+ """Verification methods supported for this transaction."""
+
+ purpose: Literal["login", "device"]
+ """Why this verification exists."""
+
+ state: Literal["requested", "ready", "sas_ready", "qr_scanned", "done", "cancelled", "error"]
+ """Current trusted-device verification state."""
+
+ error: Optional[VerificationError] = None
+ """Verification error details, if verification stopped."""
+
+ other_device: Optional[VerificationOtherDevice] = FieldInfo(alias="otherDevice", default=None)
+ """Other device participating in verification."""
+
+ other_user_id: Optional[str] = FieldInfo(alias="otherUserID", default=None)
+ """Other Beeper user participating in verification."""
+
+ qr: Optional[VerificationQr] = None
+ """QR verification data."""
+
+ sas: Optional[VerificationSAS] = None
+ """Emoji or number comparison data for verification."""
+
+
+class VerificationCreateResponse(BaseModel):
+ session: Session
+ """Current app sign-in and encrypted messaging setup state."""
+
+ verification: Optional[Verification] = None
+ """Trusted device verification progress."""
diff --git a/src/beeper_desktop_api/types/app/verification_list_response.py b/src/beeper_desktop_api/types/app/verification_list_response.py
new file mode 100644
index 0000000..8b138f4
--- /dev/null
+++ b/src/beeper_desktop_api/types/app/verification_list_response.py
@@ -0,0 +1,90 @@
+# File generated from our OpenAPI spec by Stainless. See CONTRIBUTING.md for details.
+
+from typing import List, Optional
+from typing_extensions import Literal
+
+from pydantic import Field as FieldInfo
+
+from ..._models import BaseModel
+
+__all__ = ["VerificationListResponse", "Item", "ItemError", "ItemOtherDevice", "ItemQr", "ItemSAS"]
+
+
+class ItemError(BaseModel):
+ """Verification error details, if verification stopped."""
+
+ code: str
+ """Verification error code."""
+
+ reason: str
+ """User-facing verification error message."""
+
+
+class ItemOtherDevice(BaseModel):
+ """Other device participating in verification."""
+
+ id: str
+ """Other device ID."""
+
+ name: Optional[str] = None
+ """Other device display name, if known."""
+
+
+class ItemQr(BaseModel):
+ """QR verification data."""
+
+ data: str
+ """QR code payload to display for verification."""
+
+
+class ItemSAS(BaseModel):
+ """Emoji or number comparison data for verification."""
+
+ emojis: str
+ """Emoji sequence to compare on both devices."""
+
+ decimals: Optional[str] = None
+ """Number sequence to compare on both devices."""
+
+
+class Item(BaseModel):
+ """Trusted device verification progress."""
+
+ id: str
+ """Verification ID to pass in verification action paths."""
+
+ available_actions: List[Literal["accept", "cancel", "qr.confirmScanned", "sas.start", "sas.confirm"]] = FieldInfo(
+ alias="availableActions"
+ )
+ """Verification actions that are valid for the current state."""
+
+ direction: Literal["incoming", "outgoing"]
+ """Whether this device started or received the verification."""
+
+ methods: List[Literal["qr", "sas"]]
+ """Verification methods supported for this transaction."""
+
+ purpose: Literal["login", "device"]
+ """Why this verification exists."""
+
+ state: Literal["requested", "ready", "sas_ready", "qr_scanned", "done", "cancelled", "error"]
+ """Current trusted-device verification state."""
+
+ error: Optional[ItemError] = None
+ """Verification error details, if verification stopped."""
+
+ other_device: Optional[ItemOtherDevice] = FieldInfo(alias="otherDevice", default=None)
+ """Other device participating in verification."""
+
+ other_user_id: Optional[str] = FieldInfo(alias="otherUserID", default=None)
+ """Other Beeper user participating in verification."""
+
+ qr: Optional[ItemQr] = None
+ """QR verification data."""
+
+ sas: Optional[ItemSAS] = None
+ """Emoji or number comparison data for verification."""
+
+
+class VerificationListResponse(BaseModel):
+ items: List[Item]
diff --git a/src/beeper_desktop_api/types/app/verification_retrieve_response.py b/src/beeper_desktop_api/types/app/verification_retrieve_response.py
new file mode 100644
index 0000000..baff10c
--- /dev/null
+++ b/src/beeper_desktop_api/types/app/verification_retrieve_response.py
@@ -0,0 +1,276 @@
+# File generated from our OpenAPI spec by Stainless. See CONTRIBUTING.md for details.
+
+from typing import List, Optional
+from typing_extensions import Literal
+
+from pydantic import Field as FieldInfo
+
+from ..._models import BaseModel
+
+__all__ = [
+ "VerificationRetrieveResponse",
+ "Session",
+ "SessionE2EE",
+ "SessionE2EESecrets",
+ "SessionMatrix",
+ "SessionVerification",
+ "SessionVerificationError",
+ "SessionVerificationOtherDevice",
+ "SessionVerificationQr",
+ "SessionVerificationSAS",
+ "Verification",
+ "VerificationError",
+ "VerificationOtherDevice",
+ "VerificationQr",
+ "VerificationSAS",
+]
+
+
+class SessionE2EESecrets(BaseModel):
+ """Encrypted messaging keys available on this device."""
+
+ master_key: bool = FieldInfo(alias="masterKey")
+ """Whether the account identity key is available."""
+
+ megolm_backup_key: bool = FieldInfo(alias="megolmBackupKey")
+ """Whether the encrypted message backup key is available."""
+
+ recovery_key: bool = FieldInfo(alias="recoveryKey")
+ """Whether a recovery key is available."""
+
+ self_signing_key: bool = FieldInfo(alias="selfSigningKey")
+ """Whether the device trust key is available."""
+
+ user_signing_key: bool = FieldInfo(alias="userSigningKey")
+ """Whether the user trust key is available."""
+
+
+class SessionE2EE(BaseModel):
+ """Encrypted messaging setup status."""
+
+ cross_signing: bool = FieldInfo(alias="crossSigning")
+ """Whether this account can verify trusted devices."""
+
+ first_sync_done: bool = FieldInfo(alias="firstSyncDone")
+ """Whether the first encrypted message sync is complete."""
+
+ has_backed_up_recovery_key: bool = FieldInfo(alias="hasBackedUpRecoveryKey")
+ """Whether the user confirmed that they saved their recovery key."""
+
+ initialized: bool
+ """Whether encrypted messaging setup has started."""
+
+ key_backup: bool = FieldInfo(alias="keyBackup")
+ """Whether encrypted message backup is available."""
+
+ secrets: SessionE2EESecrets
+ """Encrypted messaging keys available on this device."""
+
+ secret_storage: bool = FieldInfo(alias="secretStorage")
+ """Whether secure key storage is available."""
+
+ verified: bool
+ """Whether this device is trusted for encrypted messages."""
+
+ recovery_key_generated_at: Optional[float] = FieldInfo(alias="recoveryKeyGeneratedAt", default=None)
+ """Unix timestamp for when the recovery key was created."""
+
+
+class SessionMatrix(BaseModel):
+ """Signed-in account details. Omitted until sign-in is complete."""
+
+ device_id: str = FieldInfo(alias="deviceID")
+ """Current device ID."""
+
+ homeserver: str
+ """Beeper homeserver URL for this account."""
+
+ user_id: str = FieldInfo(alias="userID")
+ """Signed-in Beeper user ID."""
+
+
+class SessionVerificationError(BaseModel):
+ """Verification error details, if verification stopped."""
+
+ code: str
+ """Verification error code."""
+
+ reason: str
+ """User-facing verification error message."""
+
+
+class SessionVerificationOtherDevice(BaseModel):
+ """Other device participating in verification."""
+
+ id: str
+ """Other device ID."""
+
+ name: Optional[str] = None
+ """Other device display name, if known."""
+
+
+class SessionVerificationQr(BaseModel):
+ """QR verification data."""
+
+ data: str
+ """QR code payload to display for verification."""
+
+
+class SessionVerificationSAS(BaseModel):
+ """Emoji or number comparison data for verification."""
+
+ emojis: str
+ """Emoji sequence to compare on both devices."""
+
+ decimals: Optional[str] = None
+ """Number sequence to compare on both devices."""
+
+
+class SessionVerification(BaseModel):
+ """Trusted device verification progress."""
+
+ id: str
+ """Verification ID to pass in verification action paths."""
+
+ available_actions: List[Literal["accept", "cancel", "qr.confirmScanned", "sas.start", "sas.confirm"]] = FieldInfo(
+ alias="availableActions"
+ )
+ """Verification actions that are valid for the current state."""
+
+ direction: Literal["incoming", "outgoing"]
+ """Whether this device started or received the verification."""
+
+ methods: List[Literal["qr", "sas"]]
+ """Verification methods supported for this transaction."""
+
+ purpose: Literal["login", "device"]
+ """Why this verification exists."""
+
+ state: Literal["requested", "ready", "sas_ready", "qr_scanned", "done", "cancelled", "error"]
+ """Current trusted-device verification state."""
+
+ error: Optional[SessionVerificationError] = None
+ """Verification error details, if verification stopped."""
+
+ other_device: Optional[SessionVerificationOtherDevice] = FieldInfo(alias="otherDevice", default=None)
+ """Other device participating in verification."""
+
+ other_user_id: Optional[str] = FieldInfo(alias="otherUserID", default=None)
+ """Other Beeper user participating in verification."""
+
+ qr: Optional[SessionVerificationQr] = None
+ """QR verification data."""
+
+ sas: Optional[SessionVerificationSAS] = None
+ """Emoji or number comparison data for verification."""
+
+
+class Session(BaseModel):
+ """Current app sign-in and encrypted messaging setup state."""
+
+ e2ee: SessionE2EE
+ """Encrypted messaging setup status."""
+
+ state: Literal[
+ "needs-login",
+ "initializing",
+ "needs-cross-signing-setup",
+ "needs-verification",
+ "needs-secrets",
+ "needs-first-sync",
+ "ready",
+ ]
+ """
+ Current sign-in and encrypted messaging setup state for Beeper Desktop or Beeper
+ Server.
+ """
+
+ matrix: Optional[SessionMatrix] = None
+ """Signed-in account details. Omitted until sign-in is complete."""
+
+ verification: Optional[SessionVerification] = None
+ """Trusted device verification progress."""
+
+
+class VerificationError(BaseModel):
+ """Verification error details, if verification stopped."""
+
+ code: str
+ """Verification error code."""
+
+ reason: str
+ """User-facing verification error message."""
+
+
+class VerificationOtherDevice(BaseModel):
+ """Other device participating in verification."""
+
+ id: str
+ """Other device ID."""
+
+ name: Optional[str] = None
+ """Other device display name, if known."""
+
+
+class VerificationQr(BaseModel):
+ """QR verification data."""
+
+ data: str
+ """QR code payload to display for verification."""
+
+
+class VerificationSAS(BaseModel):
+ """Emoji or number comparison data for verification."""
+
+ emojis: str
+ """Emoji sequence to compare on both devices."""
+
+ decimals: Optional[str] = None
+ """Number sequence to compare on both devices."""
+
+
+class Verification(BaseModel):
+ """Trusted device verification progress."""
+
+ id: str
+ """Verification ID to pass in verification action paths."""
+
+ available_actions: List[Literal["accept", "cancel", "qr.confirmScanned", "sas.start", "sas.confirm"]] = FieldInfo(
+ alias="availableActions"
+ )
+ """Verification actions that are valid for the current state."""
+
+ direction: Literal["incoming", "outgoing"]
+ """Whether this device started or received the verification."""
+
+ methods: List[Literal["qr", "sas"]]
+ """Verification methods supported for this transaction."""
+
+ purpose: Literal["login", "device"]
+ """Why this verification exists."""
+
+ state: Literal["requested", "ready", "sas_ready", "qr_scanned", "done", "cancelled", "error"]
+ """Current trusted-device verification state."""
+
+ error: Optional[VerificationError] = None
+ """Verification error details, if verification stopped."""
+
+ other_device: Optional[VerificationOtherDevice] = FieldInfo(alias="otherDevice", default=None)
+ """Other device participating in verification."""
+
+ other_user_id: Optional[str] = FieldInfo(alias="otherUserID", default=None)
+ """Other Beeper user participating in verification."""
+
+ qr: Optional[VerificationQr] = None
+ """QR verification data."""
+
+ sas: Optional[VerificationSAS] = None
+ """Emoji or number comparison data for verification."""
+
+
+class VerificationRetrieveResponse(BaseModel):
+ session: Session
+ """Current app sign-in and encrypted messaging setup state."""
+
+ verification: Optional[Verification] = None
+ """Trusted device verification progress."""
diff --git a/src/beeper_desktop_api/types/app/verifications/__init__.py b/src/beeper_desktop_api/types/app/verifications/__init__.py
new file mode 100644
index 0000000..72e2812
--- /dev/null
+++ b/src/beeper_desktop_api/types/app/verifications/__init__.py
@@ -0,0 +1,9 @@
+# File generated from our OpenAPI spec by Stainless. See CONTRIBUTING.md for details.
+
+from __future__ import annotations
+
+from .qr_scan_params import QrScanParams as QrScanParams
+from .qr_scan_response import QrScanResponse as QrScanResponse
+from .sas_start_response import SASStartResponse as SASStartResponse
+from .sas_confirm_response import SASConfirmResponse as SASConfirmResponse
+from .qr_confirm_scanned_response import QrConfirmScannedResponse as QrConfirmScannedResponse
diff --git a/src/beeper_desktop_api/types/app/verifications/qr_confirm_scanned_response.py b/src/beeper_desktop_api/types/app/verifications/qr_confirm_scanned_response.py
new file mode 100644
index 0000000..c63b4f9
--- /dev/null
+++ b/src/beeper_desktop_api/types/app/verifications/qr_confirm_scanned_response.py
@@ -0,0 +1,276 @@
+# File generated from our OpenAPI spec by Stainless. See CONTRIBUTING.md for details.
+
+from typing import List, Optional
+from typing_extensions import Literal
+
+from pydantic import Field as FieldInfo
+
+from ...._models import BaseModel
+
+__all__ = [
+ "QrConfirmScannedResponse",
+ "Session",
+ "SessionE2EE",
+ "SessionE2EESecrets",
+ "SessionMatrix",
+ "SessionVerification",
+ "SessionVerificationError",
+ "SessionVerificationOtherDevice",
+ "SessionVerificationQr",
+ "SessionVerificationSAS",
+ "Verification",
+ "VerificationError",
+ "VerificationOtherDevice",
+ "VerificationQr",
+ "VerificationSAS",
+]
+
+
+class SessionE2EESecrets(BaseModel):
+ """Encrypted messaging keys available on this device."""
+
+ master_key: bool = FieldInfo(alias="masterKey")
+ """Whether the account identity key is available."""
+
+ megolm_backup_key: bool = FieldInfo(alias="megolmBackupKey")
+ """Whether the encrypted message backup key is available."""
+
+ recovery_key: bool = FieldInfo(alias="recoveryKey")
+ """Whether a recovery key is available."""
+
+ self_signing_key: bool = FieldInfo(alias="selfSigningKey")
+ """Whether the device trust key is available."""
+
+ user_signing_key: bool = FieldInfo(alias="userSigningKey")
+ """Whether the user trust key is available."""
+
+
+class SessionE2EE(BaseModel):
+ """Encrypted messaging setup status."""
+
+ cross_signing: bool = FieldInfo(alias="crossSigning")
+ """Whether this account can verify trusted devices."""
+
+ first_sync_done: bool = FieldInfo(alias="firstSyncDone")
+ """Whether the first encrypted message sync is complete."""
+
+ has_backed_up_recovery_key: bool = FieldInfo(alias="hasBackedUpRecoveryKey")
+ """Whether the user confirmed that they saved their recovery key."""
+
+ initialized: bool
+ """Whether encrypted messaging setup has started."""
+
+ key_backup: bool = FieldInfo(alias="keyBackup")
+ """Whether encrypted message backup is available."""
+
+ secrets: SessionE2EESecrets
+ """Encrypted messaging keys available on this device."""
+
+ secret_storage: bool = FieldInfo(alias="secretStorage")
+ """Whether secure key storage is available."""
+
+ verified: bool
+ """Whether this device is trusted for encrypted messages."""
+
+ recovery_key_generated_at: Optional[float] = FieldInfo(alias="recoveryKeyGeneratedAt", default=None)
+ """Unix timestamp for when the recovery key was created."""
+
+
+class SessionMatrix(BaseModel):
+ """Signed-in account details. Omitted until sign-in is complete."""
+
+ device_id: str = FieldInfo(alias="deviceID")
+ """Current device ID."""
+
+ homeserver: str
+ """Beeper homeserver URL for this account."""
+
+ user_id: str = FieldInfo(alias="userID")
+ """Signed-in Beeper user ID."""
+
+
+class SessionVerificationError(BaseModel):
+ """Verification error details, if verification stopped."""
+
+ code: str
+ """Verification error code."""
+
+ reason: str
+ """User-facing verification error message."""
+
+
+class SessionVerificationOtherDevice(BaseModel):
+ """Other device participating in verification."""
+
+ id: str
+ """Other device ID."""
+
+ name: Optional[str] = None
+ """Other device display name, if known."""
+
+
+class SessionVerificationQr(BaseModel):
+ """QR verification data."""
+
+ data: str
+ """QR code payload to display for verification."""
+
+
+class SessionVerificationSAS(BaseModel):
+ """Emoji or number comparison data for verification."""
+
+ emojis: str
+ """Emoji sequence to compare on both devices."""
+
+ decimals: Optional[str] = None
+ """Number sequence to compare on both devices."""
+
+
+class SessionVerification(BaseModel):
+ """Trusted device verification progress."""
+
+ id: str
+ """Verification ID to pass in verification action paths."""
+
+ available_actions: List[Literal["accept", "cancel", "qr.confirmScanned", "sas.start", "sas.confirm"]] = FieldInfo(
+ alias="availableActions"
+ )
+ """Verification actions that are valid for the current state."""
+
+ direction: Literal["incoming", "outgoing"]
+ """Whether this device started or received the verification."""
+
+ methods: List[Literal["qr", "sas"]]
+ """Verification methods supported for this transaction."""
+
+ purpose: Literal["login", "device"]
+ """Why this verification exists."""
+
+ state: Literal["requested", "ready", "sas_ready", "qr_scanned", "done", "cancelled", "error"]
+ """Current trusted-device verification state."""
+
+ error: Optional[SessionVerificationError] = None
+ """Verification error details, if verification stopped."""
+
+ other_device: Optional[SessionVerificationOtherDevice] = FieldInfo(alias="otherDevice", default=None)
+ """Other device participating in verification."""
+
+ other_user_id: Optional[str] = FieldInfo(alias="otherUserID", default=None)
+ """Other Beeper user participating in verification."""
+
+ qr: Optional[SessionVerificationQr] = None
+ """QR verification data."""
+
+ sas: Optional[SessionVerificationSAS] = None
+ """Emoji or number comparison data for verification."""
+
+
+class Session(BaseModel):
+ """Current app sign-in and encrypted messaging setup state."""
+
+ e2ee: SessionE2EE
+ """Encrypted messaging setup status."""
+
+ state: Literal[
+ "needs-login",
+ "initializing",
+ "needs-cross-signing-setup",
+ "needs-verification",
+ "needs-secrets",
+ "needs-first-sync",
+ "ready",
+ ]
+ """
+ Current sign-in and encrypted messaging setup state for Beeper Desktop or Beeper
+ Server.
+ """
+
+ matrix: Optional[SessionMatrix] = None
+ """Signed-in account details. Omitted until sign-in is complete."""
+
+ verification: Optional[SessionVerification] = None
+ """Trusted device verification progress."""
+
+
+class VerificationError(BaseModel):
+ """Verification error details, if verification stopped."""
+
+ code: str
+ """Verification error code."""
+
+ reason: str
+ """User-facing verification error message."""
+
+
+class VerificationOtherDevice(BaseModel):
+ """Other device participating in verification."""
+
+ id: str
+ """Other device ID."""
+
+ name: Optional[str] = None
+ """Other device display name, if known."""
+
+
+class VerificationQr(BaseModel):
+ """QR verification data."""
+
+ data: str
+ """QR code payload to display for verification."""
+
+
+class VerificationSAS(BaseModel):
+ """Emoji or number comparison data for verification."""
+
+ emojis: str
+ """Emoji sequence to compare on both devices."""
+
+ decimals: Optional[str] = None
+ """Number sequence to compare on both devices."""
+
+
+class Verification(BaseModel):
+ """Trusted device verification progress."""
+
+ id: str
+ """Verification ID to pass in verification action paths."""
+
+ available_actions: List[Literal["accept", "cancel", "qr.confirmScanned", "sas.start", "sas.confirm"]] = FieldInfo(
+ alias="availableActions"
+ )
+ """Verification actions that are valid for the current state."""
+
+ direction: Literal["incoming", "outgoing"]
+ """Whether this device started or received the verification."""
+
+ methods: List[Literal["qr", "sas"]]
+ """Verification methods supported for this transaction."""
+
+ purpose: Literal["login", "device"]
+ """Why this verification exists."""
+
+ state: Literal["requested", "ready", "sas_ready", "qr_scanned", "done", "cancelled", "error"]
+ """Current trusted-device verification state."""
+
+ error: Optional[VerificationError] = None
+ """Verification error details, if verification stopped."""
+
+ other_device: Optional[VerificationOtherDevice] = FieldInfo(alias="otherDevice", default=None)
+ """Other device participating in verification."""
+
+ other_user_id: Optional[str] = FieldInfo(alias="otherUserID", default=None)
+ """Other Beeper user participating in verification."""
+
+ qr: Optional[VerificationQr] = None
+ """QR verification data."""
+
+ sas: Optional[VerificationSAS] = None
+ """Emoji or number comparison data for verification."""
+
+
+class QrConfirmScannedResponse(BaseModel):
+ session: Session
+ """Current app sign-in and encrypted messaging setup state."""
+
+ verification: Optional[Verification] = None
+ """Trusted device verification progress."""
diff --git a/src/beeper_desktop_api/types/app/verifications/qr_scan_params.py b/src/beeper_desktop_api/types/app/verifications/qr_scan_params.py
new file mode 100644
index 0000000..a16f78e
--- /dev/null
+++ b/src/beeper_desktop_api/types/app/verifications/qr_scan_params.py
@@ -0,0 +1,12 @@
+# File generated from our OpenAPI spec by Stainless. See CONTRIBUTING.md for details.
+
+from __future__ import annotations
+
+from typing_extensions import Required, TypedDict
+
+__all__ = ["QrScanParams"]
+
+
+class QrScanParams(TypedDict, total=False):
+ data: Required[str]
+ """QR code payload scanned from the other device."""
diff --git a/src/beeper_desktop_api/types/app/verifications/qr_scan_response.py b/src/beeper_desktop_api/types/app/verifications/qr_scan_response.py
new file mode 100644
index 0000000..46c3399
--- /dev/null
+++ b/src/beeper_desktop_api/types/app/verifications/qr_scan_response.py
@@ -0,0 +1,276 @@
+# File generated from our OpenAPI spec by Stainless. See CONTRIBUTING.md for details.
+
+from typing import List, Optional
+from typing_extensions import Literal
+
+from pydantic import Field as FieldInfo
+
+from ...._models import BaseModel
+
+__all__ = [
+ "QrScanResponse",
+ "Session",
+ "SessionE2EE",
+ "SessionE2EESecrets",
+ "SessionMatrix",
+ "SessionVerification",
+ "SessionVerificationError",
+ "SessionVerificationOtherDevice",
+ "SessionVerificationQr",
+ "SessionVerificationSAS",
+ "Verification",
+ "VerificationError",
+ "VerificationOtherDevice",
+ "VerificationQr",
+ "VerificationSAS",
+]
+
+
+class SessionE2EESecrets(BaseModel):
+ """Encrypted messaging keys available on this device."""
+
+ master_key: bool = FieldInfo(alias="masterKey")
+ """Whether the account identity key is available."""
+
+ megolm_backup_key: bool = FieldInfo(alias="megolmBackupKey")
+ """Whether the encrypted message backup key is available."""
+
+ recovery_key: bool = FieldInfo(alias="recoveryKey")
+ """Whether a recovery key is available."""
+
+ self_signing_key: bool = FieldInfo(alias="selfSigningKey")
+ """Whether the device trust key is available."""
+
+ user_signing_key: bool = FieldInfo(alias="userSigningKey")
+ """Whether the user trust key is available."""
+
+
+class SessionE2EE(BaseModel):
+ """Encrypted messaging setup status."""
+
+ cross_signing: bool = FieldInfo(alias="crossSigning")
+ """Whether this account can verify trusted devices."""
+
+ first_sync_done: bool = FieldInfo(alias="firstSyncDone")
+ """Whether the first encrypted message sync is complete."""
+
+ has_backed_up_recovery_key: bool = FieldInfo(alias="hasBackedUpRecoveryKey")
+ """Whether the user confirmed that they saved their recovery key."""
+
+ initialized: bool
+ """Whether encrypted messaging setup has started."""
+
+ key_backup: bool = FieldInfo(alias="keyBackup")
+ """Whether encrypted message backup is available."""
+
+ secrets: SessionE2EESecrets
+ """Encrypted messaging keys available on this device."""
+
+ secret_storage: bool = FieldInfo(alias="secretStorage")
+ """Whether secure key storage is available."""
+
+ verified: bool
+ """Whether this device is trusted for encrypted messages."""
+
+ recovery_key_generated_at: Optional[float] = FieldInfo(alias="recoveryKeyGeneratedAt", default=None)
+ """Unix timestamp for when the recovery key was created."""
+
+
+class SessionMatrix(BaseModel):
+ """Signed-in account details. Omitted until sign-in is complete."""
+
+ device_id: str = FieldInfo(alias="deviceID")
+ """Current device ID."""
+
+ homeserver: str
+ """Beeper homeserver URL for this account."""
+
+ user_id: str = FieldInfo(alias="userID")
+ """Signed-in Beeper user ID."""
+
+
+class SessionVerificationError(BaseModel):
+ """Verification error details, if verification stopped."""
+
+ code: str
+ """Verification error code."""
+
+ reason: str
+ """User-facing verification error message."""
+
+
+class SessionVerificationOtherDevice(BaseModel):
+ """Other device participating in verification."""
+
+ id: str
+ """Other device ID."""
+
+ name: Optional[str] = None
+ """Other device display name, if known."""
+
+
+class SessionVerificationQr(BaseModel):
+ """QR verification data."""
+
+ data: str
+ """QR code payload to display for verification."""
+
+
+class SessionVerificationSAS(BaseModel):
+ """Emoji or number comparison data for verification."""
+
+ emojis: str
+ """Emoji sequence to compare on both devices."""
+
+ decimals: Optional[str] = None
+ """Number sequence to compare on both devices."""
+
+
+class SessionVerification(BaseModel):
+ """Trusted device verification progress."""
+
+ id: str
+ """Verification ID to pass in verification action paths."""
+
+ available_actions: List[Literal["accept", "cancel", "qr.confirmScanned", "sas.start", "sas.confirm"]] = FieldInfo(
+ alias="availableActions"
+ )
+ """Verification actions that are valid for the current state."""
+
+ direction: Literal["incoming", "outgoing"]
+ """Whether this device started or received the verification."""
+
+ methods: List[Literal["qr", "sas"]]
+ """Verification methods supported for this transaction."""
+
+ purpose: Literal["login", "device"]
+ """Why this verification exists."""
+
+ state: Literal["requested", "ready", "sas_ready", "qr_scanned", "done", "cancelled", "error"]
+ """Current trusted-device verification state."""
+
+ error: Optional[SessionVerificationError] = None
+ """Verification error details, if verification stopped."""
+
+ other_device: Optional[SessionVerificationOtherDevice] = FieldInfo(alias="otherDevice", default=None)
+ """Other device participating in verification."""
+
+ other_user_id: Optional[str] = FieldInfo(alias="otherUserID", default=None)
+ """Other Beeper user participating in verification."""
+
+ qr: Optional[SessionVerificationQr] = None
+ """QR verification data."""
+
+ sas: Optional[SessionVerificationSAS] = None
+ """Emoji or number comparison data for verification."""
+
+
+class Session(BaseModel):
+ """Current app sign-in and encrypted messaging setup state."""
+
+ e2ee: SessionE2EE
+ """Encrypted messaging setup status."""
+
+ state: Literal[
+ "needs-login",
+ "initializing",
+ "needs-cross-signing-setup",
+ "needs-verification",
+ "needs-secrets",
+ "needs-first-sync",
+ "ready",
+ ]
+ """
+ Current sign-in and encrypted messaging setup state for Beeper Desktop or Beeper
+ Server.
+ """
+
+ matrix: Optional[SessionMatrix] = None
+ """Signed-in account details. Omitted until sign-in is complete."""
+
+ verification: Optional[SessionVerification] = None
+ """Trusted device verification progress."""
+
+
+class VerificationError(BaseModel):
+ """Verification error details, if verification stopped."""
+
+ code: str
+ """Verification error code."""
+
+ reason: str
+ """User-facing verification error message."""
+
+
+class VerificationOtherDevice(BaseModel):
+ """Other device participating in verification."""
+
+ id: str
+ """Other device ID."""
+
+ name: Optional[str] = None
+ """Other device display name, if known."""
+
+
+class VerificationQr(BaseModel):
+ """QR verification data."""
+
+ data: str
+ """QR code payload to display for verification."""
+
+
+class VerificationSAS(BaseModel):
+ """Emoji or number comparison data for verification."""
+
+ emojis: str
+ """Emoji sequence to compare on both devices."""
+
+ decimals: Optional[str] = None
+ """Number sequence to compare on both devices."""
+
+
+class Verification(BaseModel):
+ """Trusted device verification progress."""
+
+ id: str
+ """Verification ID to pass in verification action paths."""
+
+ available_actions: List[Literal["accept", "cancel", "qr.confirmScanned", "sas.start", "sas.confirm"]] = FieldInfo(
+ alias="availableActions"
+ )
+ """Verification actions that are valid for the current state."""
+
+ direction: Literal["incoming", "outgoing"]
+ """Whether this device started or received the verification."""
+
+ methods: List[Literal["qr", "sas"]]
+ """Verification methods supported for this transaction."""
+
+ purpose: Literal["login", "device"]
+ """Why this verification exists."""
+
+ state: Literal["requested", "ready", "sas_ready", "qr_scanned", "done", "cancelled", "error"]
+ """Current trusted-device verification state."""
+
+ error: Optional[VerificationError] = None
+ """Verification error details, if verification stopped."""
+
+ other_device: Optional[VerificationOtherDevice] = FieldInfo(alias="otherDevice", default=None)
+ """Other device participating in verification."""
+
+ other_user_id: Optional[str] = FieldInfo(alias="otherUserID", default=None)
+ """Other Beeper user participating in verification."""
+
+ qr: Optional[VerificationQr] = None
+ """QR verification data."""
+
+ sas: Optional[VerificationSAS] = None
+ """Emoji or number comparison data for verification."""
+
+
+class QrScanResponse(BaseModel):
+ session: Session
+ """Current app sign-in and encrypted messaging setup state."""
+
+ verification: Optional[Verification] = None
+ """Trusted device verification progress."""
diff --git a/src/beeper_desktop_api/types/app/verifications/sas_confirm_response.py b/src/beeper_desktop_api/types/app/verifications/sas_confirm_response.py
new file mode 100644
index 0000000..340de20
--- /dev/null
+++ b/src/beeper_desktop_api/types/app/verifications/sas_confirm_response.py
@@ -0,0 +1,276 @@
+# File generated from our OpenAPI spec by Stainless. See CONTRIBUTING.md for details.
+
+from typing import List, Optional
+from typing_extensions import Literal
+
+from pydantic import Field as FieldInfo
+
+from ...._models import BaseModel
+
+__all__ = [
+ "SASConfirmResponse",
+ "Session",
+ "SessionE2EE",
+ "SessionE2EESecrets",
+ "SessionMatrix",
+ "SessionVerification",
+ "SessionVerificationError",
+ "SessionVerificationOtherDevice",
+ "SessionVerificationQr",
+ "SessionVerificationSAS",
+ "Verification",
+ "VerificationError",
+ "VerificationOtherDevice",
+ "VerificationQr",
+ "VerificationSAS",
+]
+
+
+class SessionE2EESecrets(BaseModel):
+ """Encrypted messaging keys available on this device."""
+
+ master_key: bool = FieldInfo(alias="masterKey")
+ """Whether the account identity key is available."""
+
+ megolm_backup_key: bool = FieldInfo(alias="megolmBackupKey")
+ """Whether the encrypted message backup key is available."""
+
+ recovery_key: bool = FieldInfo(alias="recoveryKey")
+ """Whether a recovery key is available."""
+
+ self_signing_key: bool = FieldInfo(alias="selfSigningKey")
+ """Whether the device trust key is available."""
+
+ user_signing_key: bool = FieldInfo(alias="userSigningKey")
+ """Whether the user trust key is available."""
+
+
+class SessionE2EE(BaseModel):
+ """Encrypted messaging setup status."""
+
+ cross_signing: bool = FieldInfo(alias="crossSigning")
+ """Whether this account can verify trusted devices."""
+
+ first_sync_done: bool = FieldInfo(alias="firstSyncDone")
+ """Whether the first encrypted message sync is complete."""
+
+ has_backed_up_recovery_key: bool = FieldInfo(alias="hasBackedUpRecoveryKey")
+ """Whether the user confirmed that they saved their recovery key."""
+
+ initialized: bool
+ """Whether encrypted messaging setup has started."""
+
+ key_backup: bool = FieldInfo(alias="keyBackup")
+ """Whether encrypted message backup is available."""
+
+ secrets: SessionE2EESecrets
+ """Encrypted messaging keys available on this device."""
+
+ secret_storage: bool = FieldInfo(alias="secretStorage")
+ """Whether secure key storage is available."""
+
+ verified: bool
+ """Whether this device is trusted for encrypted messages."""
+
+ recovery_key_generated_at: Optional[float] = FieldInfo(alias="recoveryKeyGeneratedAt", default=None)
+ """Unix timestamp for when the recovery key was created."""
+
+
+class SessionMatrix(BaseModel):
+ """Signed-in account details. Omitted until sign-in is complete."""
+
+ device_id: str = FieldInfo(alias="deviceID")
+ """Current device ID."""
+
+ homeserver: str
+ """Beeper homeserver URL for this account."""
+
+ user_id: str = FieldInfo(alias="userID")
+ """Signed-in Beeper user ID."""
+
+
+class SessionVerificationError(BaseModel):
+ """Verification error details, if verification stopped."""
+
+ code: str
+ """Verification error code."""
+
+ reason: str
+ """User-facing verification error message."""
+
+
+class SessionVerificationOtherDevice(BaseModel):
+ """Other device participating in verification."""
+
+ id: str
+ """Other device ID."""
+
+ name: Optional[str] = None
+ """Other device display name, if known."""
+
+
+class SessionVerificationQr(BaseModel):
+ """QR verification data."""
+
+ data: str
+ """QR code payload to display for verification."""
+
+
+class SessionVerificationSAS(BaseModel):
+ """Emoji or number comparison data for verification."""
+
+ emojis: str
+ """Emoji sequence to compare on both devices."""
+
+ decimals: Optional[str] = None
+ """Number sequence to compare on both devices."""
+
+
+class SessionVerification(BaseModel):
+ """Trusted device verification progress."""
+
+ id: str
+ """Verification ID to pass in verification action paths."""
+
+ available_actions: List[Literal["accept", "cancel", "qr.confirmScanned", "sas.start", "sas.confirm"]] = FieldInfo(
+ alias="availableActions"
+ )
+ """Verification actions that are valid for the current state."""
+
+ direction: Literal["incoming", "outgoing"]
+ """Whether this device started or received the verification."""
+
+ methods: List[Literal["qr", "sas"]]
+ """Verification methods supported for this transaction."""
+
+ purpose: Literal["login", "device"]
+ """Why this verification exists."""
+
+ state: Literal["requested", "ready", "sas_ready", "qr_scanned", "done", "cancelled", "error"]
+ """Current trusted-device verification state."""
+
+ error: Optional[SessionVerificationError] = None
+ """Verification error details, if verification stopped."""
+
+ other_device: Optional[SessionVerificationOtherDevice] = FieldInfo(alias="otherDevice", default=None)
+ """Other device participating in verification."""
+
+ other_user_id: Optional[str] = FieldInfo(alias="otherUserID", default=None)
+ """Other Beeper user participating in verification."""
+
+ qr: Optional[SessionVerificationQr] = None
+ """QR verification data."""
+
+ sas: Optional[SessionVerificationSAS] = None
+ """Emoji or number comparison data for verification."""
+
+
+class Session(BaseModel):
+ """Current app sign-in and encrypted messaging setup state."""
+
+ e2ee: SessionE2EE
+ """Encrypted messaging setup status."""
+
+ state: Literal[
+ "needs-login",
+ "initializing",
+ "needs-cross-signing-setup",
+ "needs-verification",
+ "needs-secrets",
+ "needs-first-sync",
+ "ready",
+ ]
+ """
+ Current sign-in and encrypted messaging setup state for Beeper Desktop or Beeper
+ Server.
+ """
+
+ matrix: Optional[SessionMatrix] = None
+ """Signed-in account details. Omitted until sign-in is complete."""
+
+ verification: Optional[SessionVerification] = None
+ """Trusted device verification progress."""
+
+
+class VerificationError(BaseModel):
+ """Verification error details, if verification stopped."""
+
+ code: str
+ """Verification error code."""
+
+ reason: str
+ """User-facing verification error message."""
+
+
+class VerificationOtherDevice(BaseModel):
+ """Other device participating in verification."""
+
+ id: str
+ """Other device ID."""
+
+ name: Optional[str] = None
+ """Other device display name, if known."""
+
+
+class VerificationQr(BaseModel):
+ """QR verification data."""
+
+ data: str
+ """QR code payload to display for verification."""
+
+
+class VerificationSAS(BaseModel):
+ """Emoji or number comparison data for verification."""
+
+ emojis: str
+ """Emoji sequence to compare on both devices."""
+
+ decimals: Optional[str] = None
+ """Number sequence to compare on both devices."""
+
+
+class Verification(BaseModel):
+ """Trusted device verification progress."""
+
+ id: str
+ """Verification ID to pass in verification action paths."""
+
+ available_actions: List[Literal["accept", "cancel", "qr.confirmScanned", "sas.start", "sas.confirm"]] = FieldInfo(
+ alias="availableActions"
+ )
+ """Verification actions that are valid for the current state."""
+
+ direction: Literal["incoming", "outgoing"]
+ """Whether this device started or received the verification."""
+
+ methods: List[Literal["qr", "sas"]]
+ """Verification methods supported for this transaction."""
+
+ purpose: Literal["login", "device"]
+ """Why this verification exists."""
+
+ state: Literal["requested", "ready", "sas_ready", "qr_scanned", "done", "cancelled", "error"]
+ """Current trusted-device verification state."""
+
+ error: Optional[VerificationError] = None
+ """Verification error details, if verification stopped."""
+
+ other_device: Optional[VerificationOtherDevice] = FieldInfo(alias="otherDevice", default=None)
+ """Other device participating in verification."""
+
+ other_user_id: Optional[str] = FieldInfo(alias="otherUserID", default=None)
+ """Other Beeper user participating in verification."""
+
+ qr: Optional[VerificationQr] = None
+ """QR verification data."""
+
+ sas: Optional[VerificationSAS] = None
+ """Emoji or number comparison data for verification."""
+
+
+class SASConfirmResponse(BaseModel):
+ session: Session
+ """Current app sign-in and encrypted messaging setup state."""
+
+ verification: Optional[Verification] = None
+ """Trusted device verification progress."""
diff --git a/src/beeper_desktop_api/types/app/verifications/sas_start_response.py b/src/beeper_desktop_api/types/app/verifications/sas_start_response.py
new file mode 100644
index 0000000..544c4ad
--- /dev/null
+++ b/src/beeper_desktop_api/types/app/verifications/sas_start_response.py
@@ -0,0 +1,276 @@
+# File generated from our OpenAPI spec by Stainless. See CONTRIBUTING.md for details.
+
+from typing import List, Optional
+from typing_extensions import Literal
+
+from pydantic import Field as FieldInfo
+
+from ...._models import BaseModel
+
+__all__ = [
+ "SASStartResponse",
+ "Session",
+ "SessionE2EE",
+ "SessionE2EESecrets",
+ "SessionMatrix",
+ "SessionVerification",
+ "SessionVerificationError",
+ "SessionVerificationOtherDevice",
+ "SessionVerificationQr",
+ "SessionVerificationSAS",
+ "Verification",
+ "VerificationError",
+ "VerificationOtherDevice",
+ "VerificationQr",
+ "VerificationSAS",
+]
+
+
+class SessionE2EESecrets(BaseModel):
+ """Encrypted messaging keys available on this device."""
+
+ master_key: bool = FieldInfo(alias="masterKey")
+ """Whether the account identity key is available."""
+
+ megolm_backup_key: bool = FieldInfo(alias="megolmBackupKey")
+ """Whether the encrypted message backup key is available."""
+
+ recovery_key: bool = FieldInfo(alias="recoveryKey")
+ """Whether a recovery key is available."""
+
+ self_signing_key: bool = FieldInfo(alias="selfSigningKey")
+ """Whether the device trust key is available."""
+
+ user_signing_key: bool = FieldInfo(alias="userSigningKey")
+ """Whether the user trust key is available."""
+
+
+class SessionE2EE(BaseModel):
+ """Encrypted messaging setup status."""
+
+ cross_signing: bool = FieldInfo(alias="crossSigning")
+ """Whether this account can verify trusted devices."""
+
+ first_sync_done: bool = FieldInfo(alias="firstSyncDone")
+ """Whether the first encrypted message sync is complete."""
+
+ has_backed_up_recovery_key: bool = FieldInfo(alias="hasBackedUpRecoveryKey")
+ """Whether the user confirmed that they saved their recovery key."""
+
+ initialized: bool
+ """Whether encrypted messaging setup has started."""
+
+ key_backup: bool = FieldInfo(alias="keyBackup")
+ """Whether encrypted message backup is available."""
+
+ secrets: SessionE2EESecrets
+ """Encrypted messaging keys available on this device."""
+
+ secret_storage: bool = FieldInfo(alias="secretStorage")
+ """Whether secure key storage is available."""
+
+ verified: bool
+ """Whether this device is trusted for encrypted messages."""
+
+ recovery_key_generated_at: Optional[float] = FieldInfo(alias="recoveryKeyGeneratedAt", default=None)
+ """Unix timestamp for when the recovery key was created."""
+
+
+class SessionMatrix(BaseModel):
+ """Signed-in account details. Omitted until sign-in is complete."""
+
+ device_id: str = FieldInfo(alias="deviceID")
+ """Current device ID."""
+
+ homeserver: str
+ """Beeper homeserver URL for this account."""
+
+ user_id: str = FieldInfo(alias="userID")
+ """Signed-in Beeper user ID."""
+
+
+class SessionVerificationError(BaseModel):
+ """Verification error details, if verification stopped."""
+
+ code: str
+ """Verification error code."""
+
+ reason: str
+ """User-facing verification error message."""
+
+
+class SessionVerificationOtherDevice(BaseModel):
+ """Other device participating in verification."""
+
+ id: str
+ """Other device ID."""
+
+ name: Optional[str] = None
+ """Other device display name, if known."""
+
+
+class SessionVerificationQr(BaseModel):
+ """QR verification data."""
+
+ data: str
+ """QR code payload to display for verification."""
+
+
+class SessionVerificationSAS(BaseModel):
+ """Emoji or number comparison data for verification."""
+
+ emojis: str
+ """Emoji sequence to compare on both devices."""
+
+ decimals: Optional[str] = None
+ """Number sequence to compare on both devices."""
+
+
+class SessionVerification(BaseModel):
+ """Trusted device verification progress."""
+
+ id: str
+ """Verification ID to pass in verification action paths."""
+
+ available_actions: List[Literal["accept", "cancel", "qr.confirmScanned", "sas.start", "sas.confirm"]] = FieldInfo(
+ alias="availableActions"
+ )
+ """Verification actions that are valid for the current state."""
+
+ direction: Literal["incoming", "outgoing"]
+ """Whether this device started or received the verification."""
+
+ methods: List[Literal["qr", "sas"]]
+ """Verification methods supported for this transaction."""
+
+ purpose: Literal["login", "device"]
+ """Why this verification exists."""
+
+ state: Literal["requested", "ready", "sas_ready", "qr_scanned", "done", "cancelled", "error"]
+ """Current trusted-device verification state."""
+
+ error: Optional[SessionVerificationError] = None
+ """Verification error details, if verification stopped."""
+
+ other_device: Optional[SessionVerificationOtherDevice] = FieldInfo(alias="otherDevice", default=None)
+ """Other device participating in verification."""
+
+ other_user_id: Optional[str] = FieldInfo(alias="otherUserID", default=None)
+ """Other Beeper user participating in verification."""
+
+ qr: Optional[SessionVerificationQr] = None
+ """QR verification data."""
+
+ sas: Optional[SessionVerificationSAS] = None
+ """Emoji or number comparison data for verification."""
+
+
+class Session(BaseModel):
+ """Current app sign-in and encrypted messaging setup state."""
+
+ e2ee: SessionE2EE
+ """Encrypted messaging setup status."""
+
+ state: Literal[
+ "needs-login",
+ "initializing",
+ "needs-cross-signing-setup",
+ "needs-verification",
+ "needs-secrets",
+ "needs-first-sync",
+ "ready",
+ ]
+ """
+ Current sign-in and encrypted messaging setup state for Beeper Desktop or Beeper
+ Server.
+ """
+
+ matrix: Optional[SessionMatrix] = None
+ """Signed-in account details. Omitted until sign-in is complete."""
+
+ verification: Optional[SessionVerification] = None
+ """Trusted device verification progress."""
+
+
+class VerificationError(BaseModel):
+ """Verification error details, if verification stopped."""
+
+ code: str
+ """Verification error code."""
+
+ reason: str
+ """User-facing verification error message."""
+
+
+class VerificationOtherDevice(BaseModel):
+ """Other device participating in verification."""
+
+ id: str
+ """Other device ID."""
+
+ name: Optional[str] = None
+ """Other device display name, if known."""
+
+
+class VerificationQr(BaseModel):
+ """QR verification data."""
+
+ data: str
+ """QR code payload to display for verification."""
+
+
+class VerificationSAS(BaseModel):
+ """Emoji or number comparison data for verification."""
+
+ emojis: str
+ """Emoji sequence to compare on both devices."""
+
+ decimals: Optional[str] = None
+ """Number sequence to compare on both devices."""
+
+
+class Verification(BaseModel):
+ """Trusted device verification progress."""
+
+ id: str
+ """Verification ID to pass in verification action paths."""
+
+ available_actions: List[Literal["accept", "cancel", "qr.confirmScanned", "sas.start", "sas.confirm"]] = FieldInfo(
+ alias="availableActions"
+ )
+ """Verification actions that are valid for the current state."""
+
+ direction: Literal["incoming", "outgoing"]
+ """Whether this device started or received the verification."""
+
+ methods: List[Literal["qr", "sas"]]
+ """Verification methods supported for this transaction."""
+
+ purpose: Literal["login", "device"]
+ """Why this verification exists."""
+
+ state: Literal["requested", "ready", "sas_ready", "qr_scanned", "done", "cancelled", "error"]
+ """Current trusted-device verification state."""
+
+ error: Optional[VerificationError] = None
+ """Verification error details, if verification stopped."""
+
+ other_device: Optional[VerificationOtherDevice] = FieldInfo(alias="otherDevice", default=None)
+ """Other device participating in verification."""
+
+ other_user_id: Optional[str] = FieldInfo(alias="otherUserID", default=None)
+ """Other Beeper user participating in verification."""
+
+ qr: Optional[VerificationQr] = None
+ """QR verification data."""
+
+ sas: Optional[VerificationSAS] = None
+ """Emoji or number comparison data for verification."""
+
+
+class SASStartResponse(BaseModel):
+ session: Session
+ """Current app sign-in and encrypted messaging setup state."""
+
+ verification: Optional[Verification] = None
+ """Trusted device verification progress."""
diff --git a/src/beeper_desktop_api/types/app_session_response.py b/src/beeper_desktop_api/types/app_session_response.py
new file mode 100644
index 0000000..1b5deee
--- /dev/null
+++ b/src/beeper_desktop_api/types/app_session_response.py
@@ -0,0 +1,184 @@
+# File generated from our OpenAPI spec by Stainless. See CONTRIBUTING.md for details.
+
+from typing import List, Optional
+from typing_extensions import Literal
+
+from pydantic import Field as FieldInfo
+
+from .._models import BaseModel
+
+__all__ = [
+ "AppSessionResponse",
+ "E2EE",
+ "E2EESecrets",
+ "Matrix",
+ "Verification",
+ "VerificationError",
+ "VerificationOtherDevice",
+ "VerificationQr",
+ "VerificationSAS",
+]
+
+
+class E2EESecrets(BaseModel):
+ """Encrypted messaging keys available on this device."""
+
+ master_key: bool = FieldInfo(alias="masterKey")
+ """Whether the account identity key is available."""
+
+ megolm_backup_key: bool = FieldInfo(alias="megolmBackupKey")
+ """Whether the encrypted message backup key is available."""
+
+ recovery_key: bool = FieldInfo(alias="recoveryKey")
+ """Whether a recovery key is available."""
+
+ self_signing_key: bool = FieldInfo(alias="selfSigningKey")
+ """Whether the device trust key is available."""
+
+ user_signing_key: bool = FieldInfo(alias="userSigningKey")
+ """Whether the user trust key is available."""
+
+
+class E2EE(BaseModel):
+ """Encrypted messaging setup status."""
+
+ cross_signing: bool = FieldInfo(alias="crossSigning")
+ """Whether this account can verify trusted devices."""
+
+ first_sync_done: bool = FieldInfo(alias="firstSyncDone")
+ """Whether the first encrypted message sync is complete."""
+
+ has_backed_up_recovery_key: bool = FieldInfo(alias="hasBackedUpRecoveryKey")
+ """Whether the user confirmed that they saved their recovery key."""
+
+ initialized: bool
+ """Whether encrypted messaging setup has started."""
+
+ key_backup: bool = FieldInfo(alias="keyBackup")
+ """Whether encrypted message backup is available."""
+
+ secrets: E2EESecrets
+ """Encrypted messaging keys available on this device."""
+
+ secret_storage: bool = FieldInfo(alias="secretStorage")
+ """Whether secure key storage is available."""
+
+ verified: bool
+ """Whether this device is trusted for encrypted messages."""
+
+ recovery_key_generated_at: Optional[float] = FieldInfo(alias="recoveryKeyGeneratedAt", default=None)
+ """Unix timestamp for when the recovery key was created."""
+
+
+class Matrix(BaseModel):
+ """Signed-in account details. Omitted until sign-in is complete."""
+
+ device_id: str = FieldInfo(alias="deviceID")
+ """Current device ID."""
+
+ homeserver: str
+ """Beeper homeserver URL for this account."""
+
+ user_id: str = FieldInfo(alias="userID")
+ """Signed-in Beeper user ID."""
+
+
+class VerificationError(BaseModel):
+ """Verification error details, if verification stopped."""
+
+ code: str
+ """Verification error code."""
+
+ reason: str
+ """User-facing verification error message."""
+
+
+class VerificationOtherDevice(BaseModel):
+ """Other device participating in verification."""
+
+ id: str
+ """Other device ID."""
+
+ name: Optional[str] = None
+ """Other device display name, if known."""
+
+
+class VerificationQr(BaseModel):
+ """QR verification data."""
+
+ data: str
+ """QR code payload to display for verification."""
+
+
+class VerificationSAS(BaseModel):
+ """Emoji or number comparison data for verification."""
+
+ emojis: str
+ """Emoji sequence to compare on both devices."""
+
+ decimals: Optional[str] = None
+ """Number sequence to compare on both devices."""
+
+
+class Verification(BaseModel):
+ """Trusted device verification progress."""
+
+ id: str
+ """Verification ID to pass in verification action paths."""
+
+ available_actions: List[Literal["accept", "cancel", "qr.confirmScanned", "sas.start", "sas.confirm"]] = FieldInfo(
+ alias="availableActions"
+ )
+ """Verification actions that are valid for the current state."""
+
+ direction: Literal["incoming", "outgoing"]
+ """Whether this device started or received the verification."""
+
+ methods: List[Literal["qr", "sas"]]
+ """Verification methods supported for this transaction."""
+
+ purpose: Literal["login", "device"]
+ """Why this verification exists."""
+
+ state: Literal["requested", "ready", "sas_ready", "qr_scanned", "done", "cancelled", "error"]
+ """Current trusted-device verification state."""
+
+ error: Optional[VerificationError] = None
+ """Verification error details, if verification stopped."""
+
+ other_device: Optional[VerificationOtherDevice] = FieldInfo(alias="otherDevice", default=None)
+ """Other device participating in verification."""
+
+ other_user_id: Optional[str] = FieldInfo(alias="otherUserID", default=None)
+ """Other Beeper user participating in verification."""
+
+ qr: Optional[VerificationQr] = None
+ """QR verification data."""
+
+ sas: Optional[VerificationSAS] = None
+ """Emoji or number comparison data for verification."""
+
+
+class AppSessionResponse(BaseModel):
+ e2ee: E2EE
+ """Encrypted messaging setup status."""
+
+ state: Literal[
+ "needs-login",
+ "initializing",
+ "needs-cross-signing-setup",
+ "needs-verification",
+ "needs-secrets",
+ "needs-first-sync",
+ "ready",
+ ]
+ """
+ Current sign-in and encrypted messaging setup state for Beeper Desktop or Beeper
+ Server.
+ """
+
+ matrix: Optional[Matrix] = None
+ """Signed-in account details. Omitted until sign-in is complete."""
+
+ verification: Optional[Verification] = None
+ """Trusted device verification progress."""
diff --git a/src/beeper_desktop_api/types/asset_download_params.py b/src/beeper_desktop_api/types/asset_download_params.py
index 62e7c02..58a04a1 100644
--- a/src/beeper_desktop_api/types/asset_download_params.py
+++ b/src/beeper_desktop_api/types/asset_download_params.py
@@ -9,4 +9,4 @@
class AssetDownloadParams(TypedDict, total=False):
url: Required[str]
- """Matrix content URL (mxc:// or localmxc://) for the file to download."""
+ """Beeper media URL (mxc:// or localmxc://) for the file to download."""
diff --git a/src/beeper_desktop_api/types/bridge.py b/src/beeper_desktop_api/types/bridge.py
new file mode 100644
index 0000000..38adb1b
--- /dev/null
+++ b/src/beeper_desktop_api/types/bridge.py
@@ -0,0 +1,51 @@
+# File generated from our OpenAPI spec by Stainless. See CONTRIBUTING.md for details.
+
+from typing import List, Optional
+from typing_extensions import Literal
+
+from pydantic import Field as FieldInfo
+
+from .account import Account
+from .._models import BaseModel
+
+__all__ = ["Bridge"]
+
+
+class Bridge(BaseModel):
+ """Available bridge that can connect or reconnect chat accounts."""
+
+ id: str
+ """Bridge ID. Use with bridge endpoints."""
+
+ accounts: List[Account]
+ """Connected accounts for this bridge.
+
+ Uses the same Account schema as GET /v1/accounts.
+ """
+
+ active_account_count: int = FieldInfo(alias="activeAccountCount")
+ """Number of active accounts for this network on this device."""
+
+ display_name: str = FieldInfo(alias="displayName")
+ """Human-friendly bridge name shown in Beeper."""
+
+ provider: Literal["cloud", "self-hosted", "local", "platform-sdk"]
+ """Where accounts for this bridge run: on this device or in Beeper Cloud."""
+
+ status: Literal["available", "connected", "limit_reached", "temporarily_unavailable", "disabled"]
+ """Whether this bridge can currently be used to connect new accounts."""
+
+ supports_multiple_accounts: bool = FieldInfo(alias="supportsMultipleAccounts")
+ """Whether this bridge can have multiple active accounts for the same network."""
+
+ type: str
+ """
+ Underlying bridge type, such as matrix, discordgo, slackgo, whatsapp, telegram,
+ or twitter.
+ """
+
+ network: Optional[str] = None
+ """Network grouping used for account counts and limits."""
+
+ status_text: Optional[str] = FieldInfo(alias="statusText", default=None)
+ """Human-friendly status text matching Beeper account management language."""
diff --git a/src/beeper_desktop_api/types/bridge_list_response.py b/src/beeper_desktop_api/types/bridge_list_response.py
new file mode 100644
index 0000000..ed0d497
--- /dev/null
+++ b/src/beeper_desktop_api/types/bridge_list_response.py
@@ -0,0 +1,14 @@
+# File generated from our OpenAPI spec by Stainless. See CONTRIBUTING.md for details.
+
+from typing import List
+
+from .bridge import Bridge
+from .._models import BaseModel
+
+__all__ = ["BridgeListResponse"]
+
+
+class BridgeListResponse(BaseModel):
+ """Available bridges and their connected accounts."""
+
+ items: List[Bridge]
diff --git a/src/beeper_desktop_api/types/bridge_retrieve_response.py b/src/beeper_desktop_api/types/bridge_retrieve_response.py
new file mode 100644
index 0000000..89b4512
--- /dev/null
+++ b/src/beeper_desktop_api/types/bridge_retrieve_response.py
@@ -0,0 +1,51 @@
+# File generated from our OpenAPI spec by Stainless. See CONTRIBUTING.md for details.
+
+from typing import List, Optional
+from typing_extensions import Literal
+
+from pydantic import Field as FieldInfo
+
+from .account import Account
+from .._models import BaseModel
+
+__all__ = ["BridgeRetrieveResponse"]
+
+
+class BridgeRetrieveResponse(BaseModel):
+ """Available bridge that can connect or reconnect chat accounts."""
+
+ id: str
+ """Bridge ID. Use with bridge endpoints."""
+
+ accounts: List[Account]
+ """Connected accounts for this bridge.
+
+ Uses the same Account schema as GET /v1/accounts.
+ """
+
+ active_account_count: int = FieldInfo(alias="activeAccountCount")
+ """Number of active accounts for this network on this device."""
+
+ display_name: str = FieldInfo(alias="displayName")
+ """Human-friendly bridge name shown in Beeper."""
+
+ provider: Literal["cloud", "self-hosted", "local", "platform-sdk"]
+ """Where accounts for this bridge run: on this device or in Beeper Cloud."""
+
+ status: Literal["available", "connected", "limit_reached", "temporarily_unavailable", "disabled"]
+ """Whether this bridge can currently be used to connect new accounts."""
+
+ supports_multiple_accounts: bool = FieldInfo(alias="supportsMultipleAccounts")
+ """Whether this bridge can have multiple active accounts for the same network."""
+
+ type: str
+ """
+ Underlying bridge type, such as matrix, discordgo, slackgo, whatsapp, telegram,
+ or twitter.
+ """
+
+ network: Optional[str] = None
+ """Network grouping used for account counts and limits."""
+
+ status_text: Optional[str] = FieldInfo(alias="statusText", default=None)
+ """Human-friendly status text matching Beeper account management language."""
diff --git a/src/beeper_desktop_api/types/bridges/__init__.py b/src/beeper_desktop_api/types/bridges/__init__.py
new file mode 100644
index 0000000..4efdabc
--- /dev/null
+++ b/src/beeper_desktop_api/types/bridges/__init__.py
@@ -0,0 +1,7 @@
+# File generated from our OpenAPI spec by Stainless. See CONTRIBUTING.md for details.
+
+from __future__ import annotations
+
+from .login_flow_list_response import LoginFlowListResponse as LoginFlowListResponse
+from .login_session_create_params import LoginSessionCreateParams as LoginSessionCreateParams
+from .login_session_cancel_response import LoginSessionCancelResponse as LoginSessionCancelResponse
diff --git a/src/beeper_desktop_api/types/bridges/login_flow_list_response.py b/src/beeper_desktop_api/types/bridges/login_flow_list_response.py
new file mode 100644
index 0000000..1d625e3
--- /dev/null
+++ b/src/beeper_desktop_api/types/bridges/login_flow_list_response.py
@@ -0,0 +1,12 @@
+# File generated from our OpenAPI spec by Stainless. See CONTRIBUTING.md for details.
+
+from typing import List
+
+from ..._models import BaseModel
+from ..login_flow import LoginFlow
+
+__all__ = ["LoginFlowListResponse"]
+
+
+class LoginFlowListResponse(BaseModel):
+ items: List[LoginFlow]
diff --git a/src/beeper_desktop_api/types/bridges/login_session_cancel_response.py b/src/beeper_desktop_api/types/bridges/login_session_cancel_response.py
new file mode 100644
index 0000000..a3aaee6
--- /dev/null
+++ b/src/beeper_desktop_api/types/bridges/login_session_cancel_response.py
@@ -0,0 +1,17 @@
+# File generated from our OpenAPI spec by Stainless. See CONTRIBUTING.md for details.
+
+from typing_extensions import Literal
+
+from pydantic import Field as FieldInfo
+
+from ..._models import BaseModel
+
+__all__ = ["LoginSessionCancelResponse"]
+
+
+class LoginSessionCancelResponse(BaseModel):
+ bridge_id: str = FieldInfo(alias="bridgeID")
+
+ login_session_id: str = FieldInfo(alias="loginSessionID")
+
+ status: Literal["cancelled"]
diff --git a/src/beeper_desktop_api/types/bridges/login_session_create_params.py b/src/beeper_desktop_api/types/bridges/login_session_create_params.py
new file mode 100644
index 0000000..8da4234
--- /dev/null
+++ b/src/beeper_desktop_api/types/bridges/login_session_create_params.py
@@ -0,0 +1,23 @@
+# File generated from our OpenAPI spec by Stainless. See CONTRIBUTING.md for details.
+
+from __future__ import annotations
+
+from typing_extensions import Annotated, TypedDict
+
+from ..._utils import PropertyInfo
+
+__all__ = ["LoginSessionCreateParams"]
+
+
+class LoginSessionCreateParams(TypedDict, total=False):
+ account_id: Annotated[str, PropertyInfo(alias="accountID")]
+ """Existing chat account ID to reconnect. Omit to connect a new account."""
+
+ flow_id: Annotated[str, PropertyInfo(alias="flowID")]
+ """Optional flow ID returned by the list login flows endpoint.
+
+ If omitted, Beeper chooses the default flow.
+ """
+
+ login_id: Annotated[str, PropertyInfo(alias="loginID")]
+ """Existing bridge login ID to reconnect. Omit to connect a new account."""
diff --git a/src/beeper_desktop_api/types/bridges/login_sessions/__init__.py b/src/beeper_desktop_api/types/bridges/login_sessions/__init__.py
new file mode 100644
index 0000000..a83b5fb
--- /dev/null
+++ b/src/beeper_desktop_api/types/bridges/login_sessions/__init__.py
@@ -0,0 +1,5 @@
+# File generated from our OpenAPI spec by Stainless. See CONTRIBUTING.md for details.
+
+from __future__ import annotations
+
+from .step_submit_params import StepSubmitParams as StepSubmitParams
diff --git a/src/beeper_desktop_api/types/bridges/login_sessions/step_submit_params.py b/src/beeper_desktop_api/types/bridges/login_sessions/step_submit_params.py
new file mode 100644
index 0000000..e099810
--- /dev/null
+++ b/src/beeper_desktop_api/types/bridges/login_sessions/step_submit_params.py
@@ -0,0 +1,33 @@
+# File generated from our OpenAPI spec by Stainless. See CONTRIBUTING.md for details.
+
+from __future__ import annotations
+
+from typing import Dict
+from typing_extensions import Literal, Required, Annotated, TypedDict
+
+from ...._utils import PropertyInfo
+
+__all__ = ["StepSubmitParams"]
+
+
+class StepSubmitParams(TypedDict, total=False):
+ bridge_id: Required[Annotated[str, PropertyInfo(alias="bridgeID")]]
+ """Bridge ID."""
+
+ login_session_id: Required[Annotated[str, PropertyInfo(alias="loginSessionID")]]
+ """Temporary bridge login session ID."""
+
+ type: Required[Literal["user_input", "cookies", "display_and_wait"]]
+
+ fields: Dict[str, str]
+ """Field values keyed by the field IDs from the current step."""
+
+ last_url: Annotated[str, PropertyInfo(alias="lastURL")]
+ """Last browser URL reached during a cookies step, if available."""
+
+ source: Literal["api", "webview", "browser_extension"]
+ """How the step was completed.
+
+ Omit unless the client needs to distinguish an embedded webview or browser
+ extension.
+ """
diff --git a/src/beeper_desktop_api/types/chat.py b/src/beeper_desktop_api/types/chat.py
index 4cd922c..788c891 100644
--- a/src/beeper_desktop_api/types/chat.py
+++ b/src/beeper_desktop_api/types/chat.py
@@ -38,7 +38,7 @@ class ParticipantsItem(User):
"""True if this participant has admin privileges in the chat."""
is_network_bot: Optional[bool] = FieldInfo(alias="isNetworkBot", default=None)
- """True if this participant represents a network or bridge bot."""
+ """True if this participant represents an automated network account."""
is_pending: Optional[bool] = FieldInfo(alias="isPending", default=None)
"""True if this participant has been invited but has not joined yet."""
@@ -370,7 +370,7 @@ class Draft(BaseModel):
"""Current draft object for this chat, or null when no draft is set."""
text: str
- """Matrix HTML draft body."""
+ """Rich-text draft body as returned by Beeper."""
attachments: Optional[Dict[str, DraftAttachments]] = None
"""Draft attachments keyed by attachment ID."""
@@ -455,7 +455,7 @@ class Chat(BaseModel):
"""Last read message sortKey."""
local_chat_id: Optional[str] = FieldInfo(alias="localChatID", default=None)
- """Local chat ID specific to this Beeper Desktop installation."""
+ """Local chat ID specific to this installation."""
message_expiry_seconds: Optional[int] = FieldInfo(alias="messageExpirySeconds", default=None)
"""Disappearing-message timer in seconds when available."""
diff --git a/src/beeper_desktop_api/types/chat_search_params.py b/src/beeper_desktop_api/types/chat_search_params.py
index 661e9de..b3d8876 100644
--- a/src/beeper_desktop_api/types/chat_search_params.py
+++ b/src/beeper_desktop_api/types/chat_search_params.py
@@ -14,10 +14,7 @@
class ChatSearchParams(TypedDict, total=False):
account_ids: Annotated[SequenceNotStr[str], PropertyInfo(alias="accountIDs")]
- """
- Provide an array of account IDs to filter chats from specific messaging accounts
- only
- """
+ """Limit results to specific chat accounts."""
cursor: str
"""Opaque pagination cursor; do not inspect. Use together with 'direction'."""
@@ -41,25 +38,19 @@ class ChatSearchParams(TypedDict, total=False):
"""
last_activity_after: Annotated[Union[str, datetime], PropertyInfo(alias="lastActivityAfter", format="iso8601")]
- """
- Provide an ISO datetime string to only retrieve chats with last activity after
- this time
- """
+ """Only include chats with last activity after this ISO 8601 datetime."""
last_activity_before: Annotated[Union[str, datetime], PropertyInfo(alias="lastActivityBefore", format="iso8601")]
- """
- Provide an ISO datetime string to only retrieve chats with last activity before
- this time
- """
+ """Only include chats with last activity before this ISO 8601 datetime."""
limit: int
"""Set the maximum number of chats to retrieve. Valid range: 1-200, default is 50"""
query: str
- """Literal token search (non-semantic).
+ """Literal chat search.
- Use single words users type (e.g., "dinner"). When multiple words provided, ALL
- must match. Case-insensitive.
+ Use words the user typed, such as "dinner". When multiple words are provided,
+ all must match. Case-insensitive.
"""
scope: Literal["titles", "participants"]
diff --git a/src/beeper_desktop_api/types/chat_start_params.py b/src/beeper_desktop_api/types/chat_start_params.py
index e70216d..69030f1 100644
--- a/src/beeper_desktop_api/types/chat_start_params.py
+++ b/src/beeper_desktop_api/types/chat_start_params.py
@@ -14,7 +14,7 @@ class ChatStartParams(TypedDict, total=False):
"""Account to create or start the chat on."""
user: Required[User]
- """Merged user-like contact payload used to resolve the best identifier."""
+ """Contact-like user payload used to resolve the best identifier."""
allow_invite: Annotated[bool, PropertyInfo(alias="allowInvite")]
"""Whether invite-based DM creation is allowed when required by the platform."""
@@ -24,7 +24,7 @@ class ChatStartParams(TypedDict, total=False):
class User(TypedDict, total=False):
- """Merged user-like contact payload used to resolve the best identifier."""
+ """Contact-like user payload used to resolve the best identifier."""
id: str
"""Known user ID when available."""
diff --git a/src/beeper_desktop_api/types/chat_update_params.py b/src/beeper_desktop_api/types/chat_update_params.py
index d485a1b..1e6da03 100644
--- a/src/beeper_desktop_api/types/chat_update_params.py
+++ b/src/beeper_desktop_api/types/chat_update_params.py
@@ -95,8 +95,8 @@ class Draft(TypedDict, total=False):
text: Required[str]
"""Draft text.
- Plain text and Markdown are converted to Matrix HTML with the same rules used by
- send and edit.
+ Plain text and Markdown are converted to Beeper rich text with the same rules
+ used by send and edit.
"""
attachments: Dict[str, DraftAttachments]
diff --git a/src/beeper_desktop_api/types/chats/messages/reaction_add_params.py b/src/beeper_desktop_api/types/chats/messages/reaction_add_params.py
index 976e0a0..24b3585 100644
--- a/src/beeper_desktop_api/types/chats/messages/reaction_add_params.py
+++ b/src/beeper_desktop_api/types/chats/messages/reaction_add_params.py
@@ -13,8 +13,8 @@ class ReactionAddParams(TypedDict, total=False):
chat_id: Required[Annotated[str, PropertyInfo(alias="chatID")]]
"""Chat ID.
- Input routes also accept the local chat ID from this Beeper Desktop installation
- when available.
+ Input routes also accept the local chat ID from this installation when
+ available.
"""
reaction_key: Required[Annotated[str, PropertyInfo(alias="reactionKey")]]
diff --git a/src/beeper_desktop_api/types/chats/messages/reaction_add_response.py b/src/beeper_desktop_api/types/chats/messages/reaction_add_response.py
index 3e0a638..d465e10 100644
--- a/src/beeper_desktop_api/types/chats/messages/reaction_add_response.py
+++ b/src/beeper_desktop_api/types/chats/messages/reaction_add_response.py
@@ -13,8 +13,8 @@ class ReactionAddResponse(BaseModel):
chat_id: str = FieldInfo(alias="chatID")
"""Chat ID.
- Input routes also accept the local chat ID from this Beeper Desktop installation
- when available.
+ Input routes also accept the local chat ID from this installation when
+ available.
"""
message_id: str = FieldInfo(alias="messageID")
diff --git a/src/beeper_desktop_api/types/chats/messages/reaction_delete_response.py b/src/beeper_desktop_api/types/chats/messages/reaction_delete_response.py
index 196a739..48b65de 100644
--- a/src/beeper_desktop_api/types/chats/messages/reaction_delete_response.py
+++ b/src/beeper_desktop_api/types/chats/messages/reaction_delete_response.py
@@ -13,8 +13,8 @@ class ReactionDeleteResponse(BaseModel):
chat_id: str = FieldInfo(alias="chatID")
"""Chat ID.
- Input routes also accept the local chat ID from this Beeper Desktop installation
- when available.
+ Input routes also accept the local chat ID from this installation when
+ available.
"""
message_id: str = FieldInfo(alias="messageID")
diff --git a/src/beeper_desktop_api/types/client_focus_params.py b/src/beeper_desktop_api/types/client_focus_params.py
index df3106f..c8e0d9d 100644
--- a/src/beeper_desktop_api/types/client_focus_params.py
+++ b/src/beeper_desktop_api/types/client_focus_params.py
@@ -17,7 +17,7 @@ class ClientFocusParams(TypedDict, total=False):
"""
draft_attachment_path: Annotated[str, PropertyInfo(alias="draftAttachmentPath")]
- """Optional image path to populate in the message input field."""
+ """Optional local image path to populate in the message input field."""
draft_text: Annotated[str, PropertyInfo(alias="draftText")]
"""Optional plain text to populate in the message input field."""
diff --git a/src/beeper_desktop_api/types/client_search_params.py b/src/beeper_desktop_api/types/client_search_params.py
index 6135164..fb7f161 100644
--- a/src/beeper_desktop_api/types/client_search_params.py
+++ b/src/beeper_desktop_api/types/client_search_params.py
@@ -9,4 +9,4 @@
class ClientSearchParams(TypedDict, total=False):
query: Required[str]
- """User-typed search text. Literal word matching (non-semantic)."""
+ """User-typed search text. Uses literal word matching."""
diff --git a/src/beeper_desktop_api/types/cookie_field.py b/src/beeper_desktop_api/types/cookie_field.py
new file mode 100644
index 0000000..5c20149
--- /dev/null
+++ b/src/beeper_desktop_api/types/cookie_field.py
@@ -0,0 +1,19 @@
+# File generated from our OpenAPI spec by Stainless. See CONTRIBUTING.md for details.
+
+from typing import Optional
+from typing_extensions import Literal
+
+from .._models import BaseModel
+
+__all__ = ["CookieField"]
+
+
+class CookieField(BaseModel):
+ id: str
+ """Field ID to send back in the fields object."""
+
+ name: Optional[str] = None
+ """Cookie, header, or local storage key to collect."""
+
+ type: Optional[Literal["cookie", "header", "local_storage"]] = None
+ """Browser storage source for this value."""
diff --git a/src/beeper_desktop_api/types/disappearing_timer_capability.py b/src/beeper_desktop_api/types/disappearing_timer_capability.py
new file mode 100644
index 0000000..9e86c25
--- /dev/null
+++ b/src/beeper_desktop_api/types/disappearing_timer_capability.py
@@ -0,0 +1,18 @@
+# File generated from our OpenAPI spec by Stainless. See CONTRIBUTING.md for details.
+
+from typing import List, Optional
+from typing_extensions import Literal
+
+from .._models import BaseModel
+
+__all__ = ["DisappearingTimerCapability"]
+
+
+class DisappearingTimerCapability(BaseModel):
+ """Disappearing-message timer capability."""
+
+ types: List[Literal["", "after_read", "after_send"]]
+
+ omit_empty_timer: Optional[Literal[True]] = None
+
+ timers: Optional[List[int]] = None
diff --git a/src/beeper_desktop_api/types/group_field_capability.py b/src/beeper_desktop_api/types/group_field_capability.py
new file mode 100644
index 0000000..2ecbb92
--- /dev/null
+++ b/src/beeper_desktop_api/types/group_field_capability.py
@@ -0,0 +1,23 @@
+# File generated from our OpenAPI spec by Stainless. See CONTRIBUTING.md for details.
+
+from typing import Optional
+
+from .._models import BaseModel
+from .disappearing_timer_capability import DisappearingTimerCapability
+
+__all__ = ["GroupFieldCapability"]
+
+
+class GroupFieldCapability(BaseModel):
+ """Group creation field capability."""
+
+ allowed: bool
+
+ max_length: Optional[int] = None
+
+ min_length: Optional[int] = None
+
+ required: Optional[bool] = None
+
+ settings: Optional[DisappearingTimerCapability] = None
+ """Disappearing-message timer capability."""
diff --git a/src/beeper_desktop_api/types/group_type_capabilities.py b/src/beeper_desktop_api/types/group_type_capabilities.py
new file mode 100644
index 0000000..abb841f
--- /dev/null
+++ b/src/beeper_desktop_api/types/group_type_capabilities.py
@@ -0,0 +1,35 @@
+# File generated from our OpenAPI spec by Stainless. See CONTRIBUTING.md for details.
+
+from typing import Optional
+
+from .._models import BaseModel
+from .group_field_capability import GroupFieldCapability
+
+__all__ = ["GroupTypeCapabilities"]
+
+
+class GroupTypeCapabilities(BaseModel):
+ """Group creation capabilities for one group type."""
+
+ type_description: str
+
+ avatar: Optional[GroupFieldCapability] = None
+ """Group creation field capability."""
+
+ disappear: Optional[GroupFieldCapability] = None
+ """Group creation field capability."""
+
+ name: Optional[GroupFieldCapability] = None
+ """Group creation field capability."""
+
+ parent: Optional[GroupFieldCapability] = None
+ """Group creation field capability."""
+
+ participants: Optional[GroupFieldCapability] = None
+ """Group creation field capability."""
+
+ topic: Optional[GroupFieldCapability] = None
+ """Group creation field capability."""
+
+ username: Optional[GroupFieldCapability] = None
+ """Group creation field capability."""
diff --git a/src/beeper_desktop_api/types/info_retrieve_response.py b/src/beeper_desktop_api/types/info_retrieve_response.py
index 9a643aa..74124de 100644
--- a/src/beeper_desktop_api/types/info_retrieve_response.py
+++ b/src/beeper_desktop_api/types/info_retrieve_response.py
@@ -64,7 +64,7 @@ class Platform(BaseModel):
class Server(BaseModel):
base_url: str
- """Base URL of the Beeper Desktop API server"""
+ """Base URL of the Beeper Client API server"""
hostname: str
"""Listening host"""
diff --git a/src/beeper_desktop_api/types/login_flow.py b/src/beeper_desktop_api/types/login_flow.py
new file mode 100644
index 0000000..fe0c71b
--- /dev/null
+++ b/src/beeper_desktop_api/types/login_flow.py
@@ -0,0 +1,20 @@
+# File generated from our OpenAPI spec by Stainless. See CONTRIBUTING.md for details.
+
+from typing import Optional
+
+from .._models import BaseModel
+
+__all__ = ["LoginFlow"]
+
+
+class LoginFlow(BaseModel):
+ """Connect or reconnect flow option for a bridge."""
+
+ id: str
+ """Flow ID to pass when creating a bridge login session."""
+
+ description: Optional[str] = None
+ """Short explanation for when to use this flow, when provided."""
+
+ name: Optional[str] = None
+ """Display name for the flow, when provided."""
diff --git a/src/beeper_desktop_api/types/login_input_field.py b/src/beeper_desktop_api/types/login_input_field.py
new file mode 100644
index 0000000..4407f27
--- /dev/null
+++ b/src/beeper_desktop_api/types/login_input_field.py
@@ -0,0 +1,29 @@
+# File generated from our OpenAPI spec by Stainless. See CONTRIBUTING.md for details.
+
+from typing import Optional
+
+from pydantic import Field as FieldInfo
+
+from .._models import BaseModel
+
+__all__ = ["LoginInputField"]
+
+
+class LoginInputField(BaseModel):
+ id: str
+ """Field ID to send back in the fields object."""
+
+ initial_value: Optional[str] = FieldInfo(alias="initialValue", default=None)
+ """Initial field value, when provided by the network."""
+
+ label: Optional[str] = None
+ """Field label to show to the user."""
+
+ optional: Optional[bool] = None
+ """True if the user can leave this field empty."""
+
+ placeholder: Optional[str] = None
+ """Placeholder text to show when the field is empty."""
+
+ type: Optional[str] = None
+ """Suggested input type, such as text, password, or email."""
diff --git a/src/beeper_desktop_api/types/login_session.py b/src/beeper_desktop_api/types/login_session.py
new file mode 100644
index 0000000..1a5fe9f
--- /dev/null
+++ b/src/beeper_desktop_api/types/login_session.py
@@ -0,0 +1,207 @@
+# File generated from our OpenAPI spec by Stainless. See CONTRIBUTING.md for details.
+
+from typing import List, Union, Optional
+from typing_extensions import Literal, TypeAlias
+
+from pydantic import Field as FieldInfo
+
+from .account import Account
+from .._models import BaseModel
+from .shared.user import User
+from .cookie_field import CookieField
+from .shared.api_error import APIError
+from .login_input_field import LoginInputField
+
+__all__ = [
+ "LoginSession",
+ "CurrentStep",
+ "CurrentStepUserInput",
+ "CurrentStepCookies",
+ "CurrentStepDisplayAndWait",
+ "CurrentStepDisplayAndWaitDisplay",
+ "CurrentStepDisplayAndWaitDisplayQrCode",
+ "CurrentStepDisplayAndWaitDisplayEmoji",
+ "CurrentStepDisplayAndWaitDisplayEmpty",
+ "CurrentStepComplete",
+ "CurrentStepCompleteLogin",
+ "Login",
+]
+
+
+class CurrentStepUserInput(BaseModel):
+ fields: List[LoginInputField]
+
+ step_id: str = FieldInfo(alias="stepID")
+
+ type: Literal["user_input"]
+
+ attachments: Optional[List[Optional[object]]] = None
+
+ instructions: Optional[str] = None
+ """User-facing instructions for this step."""
+
+
+class CurrentStepCookies(BaseModel):
+ fields: List[CookieField]
+
+ step_id: str = FieldInfo(alias="stepID")
+
+ type: Literal["cookies"]
+
+ url: str
+ """URL to open for the user."""
+
+ expected_final_url_regex: Optional[str] = FieldInfo(alias="expectedFinalURLRegex", default=None)
+ """Regular expression that identifies the final URL after sign-in."""
+
+ extract_js: Optional[str] = FieldInfo(alias="extractJS", default=None)
+ """Optional extraction script for browser-based sign-in helpers.
+
+ Treat as an opaque helper value.
+ """
+
+ instructions: Optional[str] = None
+ """User-facing instructions for this browser step."""
+
+ user_agent: Optional[str] = FieldInfo(alias="userAgent", default=None)
+ """Suggested user agent for the browser session."""
+
+
+class CurrentStepDisplayAndWaitDisplayQrCode(BaseModel):
+ data: str
+
+ type: Literal["qr"]
+
+
+class CurrentStepDisplayAndWaitDisplayEmoji(BaseModel):
+ image_url: str = FieldInfo(alias="imageURL")
+
+ type: Literal["emoji"]
+
+
+class CurrentStepDisplayAndWaitDisplayEmpty(BaseModel):
+ type: Literal["nothing"]
+
+
+CurrentStepDisplayAndWaitDisplay: TypeAlias = Union[
+ CurrentStepDisplayAndWaitDisplayQrCode, CurrentStepDisplayAndWaitDisplayEmoji, CurrentStepDisplayAndWaitDisplayEmpty
+]
+
+
+class CurrentStepDisplayAndWait(BaseModel):
+ display: CurrentStepDisplayAndWaitDisplay
+
+ step_id: str = FieldInfo(alias="stepID")
+
+ type: Literal["display_and_wait"]
+
+ instructions: Optional[str] = None
+ """User-facing instructions for this step."""
+
+
+class CurrentStepCompleteLogin(BaseModel):
+ """Signed-in identity for a bridge.
+
+ One bridge login can contain multiple chat accounts.
+ """
+
+ bridge_id: str = FieldInfo(alias="bridgeID")
+ """Bridge ID."""
+
+ login_id: str = FieldInfo(alias="loginID")
+ """Bridge login ID."""
+
+ remove_scopes: List[Literal["current-device", "all-devices"]] = FieldInfo(alias="removeScopes")
+
+ status: Literal["connected", "connecting", "needs_login", "logged_out", "unknown"]
+
+ account_ids: Optional[List[str]] = FieldInfo(alias="accountIDs", default=None)
+ """Chat accounts that belong to this bridge login, when known."""
+
+ status_text: Optional[str] = FieldInfo(alias="statusText", default=None)
+ """Human-friendly bridge login status text."""
+
+ user: Optional[User] = None
+ """User the account belongs to."""
+
+
+class CurrentStepComplete(BaseModel):
+ type: Literal["complete"]
+
+ account: Optional[Account] = None
+ """A chat account added to Beeper."""
+
+ instructions: Optional[str] = None
+ """Completion instructions, when provided."""
+
+ login: Optional[CurrentStepCompleteLogin] = None
+ """Signed-in identity for a bridge.
+
+ One bridge login can contain multiple chat accounts.
+ """
+
+ step_id: Optional[str] = FieldInfo(alias="stepID", default=None)
+
+
+CurrentStep: TypeAlias = Union[CurrentStepUserInput, CurrentStepCookies, CurrentStepDisplayAndWait, CurrentStepComplete]
+
+
+class Login(BaseModel):
+ """Signed-in identity for a bridge.
+
+ One bridge login can contain multiple chat accounts.
+ """
+
+ bridge_id: str = FieldInfo(alias="bridgeID")
+ """Bridge ID."""
+
+ login_id: str = FieldInfo(alias="loginID")
+ """Bridge login ID."""
+
+ remove_scopes: List[Literal["current-device", "all-devices"]] = FieldInfo(alias="removeScopes")
+
+ status: Literal["connected", "connecting", "needs_login", "logged_out", "unknown"]
+
+ account_ids: Optional[List[str]] = FieldInfo(alias="accountIDs", default=None)
+ """Chat accounts that belong to this bridge login, when known."""
+
+ status_text: Optional[str] = FieldInfo(alias="statusText", default=None)
+ """Human-friendly bridge login status text."""
+
+ user: Optional[User] = None
+ """User the account belongs to."""
+
+
+class LoginSession(BaseModel):
+ bridge_id: str = FieldInfo(alias="bridgeID")
+ """Bridge ID."""
+
+ login_session_id: str = FieldInfo(alias="loginSessionID")
+ """Temporary bridge login session ID."""
+
+ status: Literal[
+ "waiting_for_input", "waiting_for_cookies", "waiting_for_display", "complete", "cancelled", "failed"
+ ]
+
+ account: Optional[Account] = None
+ """A chat account added to Beeper."""
+
+ account_id: Optional[str] = FieldInfo(alias="accountID", default=None)
+ """Chat account ID for reconnect flows, when known."""
+
+ current_step: Optional[CurrentStep] = FieldInfo(alias="currentStep", default=None)
+ """Step the client should show or complete next.
+
+ Omitted when the session is complete, cancelled, or failed.
+ """
+
+ error: Optional[APIError] = None
+
+ login: Optional[Login] = None
+ """Signed-in identity for a bridge.
+
+ One bridge login can contain multiple chat accounts.
+ """
+
+ login_id: Optional[str] = FieldInfo(alias="loginID", default=None)
+ """Bridge login ID for reconnect flows, when known."""
diff --git a/src/beeper_desktop_api/types/message_delete_params.py b/src/beeper_desktop_api/types/message_delete_params.py
index f8c7fff..ee4ca5e 100644
--- a/src/beeper_desktop_api/types/message_delete_params.py
+++ b/src/beeper_desktop_api/types/message_delete_params.py
@@ -14,8 +14,8 @@ class MessageDeleteParams(TypedDict, total=False):
chat_id: Required[Annotated[str, PropertyInfo(alias="chatID")]]
"""Chat ID.
- Input routes also accept the local chat ID from this Beeper Desktop installation
- when available.
+ Input routes also accept the local chat ID from this installation when
+ available.
"""
for_everyone: Annotated[Optional[bool], PropertyInfo(alias="forEveryone")]
diff --git a/src/beeper_desktop_api/types/message_search_params.py b/src/beeper_desktop_api/types/message_search_params.py
index e9bab35..09b3336 100644
--- a/src/beeper_desktop_api/types/message_search_params.py
+++ b/src/beeper_desktop_api/types/message_search_params.py
@@ -66,12 +66,11 @@ class MessageSearchParams(TypedDict, total=False):
"""
query: str
- """Literal word search (non-semantic).
+ """Literal word search.
- Finds messages containing these EXACT words in any order. Use single words users
- actually type, not concepts or phrases. Example: use "dinner" not "dinner
- plans", use "sick" not "health issues". If omitted, returns results filtered
- only by other parameters.
+ Finds messages containing these words in any order. Use words the user actually
+ typed, not inferred concepts. Example: use "dinner" rather than "dinner plans".
+ If omitted, returns results filtered only by the other parameters.
"""
sender: str
diff --git a/src/beeper_desktop_api/types/message_send_params.py b/src/beeper_desktop_api/types/message_send_params.py
index 78de9d1..f73d26d 100644
--- a/src/beeper_desktop_api/types/message_send_params.py
+++ b/src/beeper_desktop_api/types/message_send_params.py
@@ -19,8 +19,8 @@ class MessageSendParams(TypedDict, total=False):
text: str
"""Draft text.
- Plain text and Markdown are converted to Matrix HTML with the same rules used by
- send and edit.
+ Plain text and Markdown are converted to Beeper rich text with the same rules
+ used by send and edit.
"""
diff --git a/src/beeper_desktop_api/types/message_send_response.py b/src/beeper_desktop_api/types/message_send_response.py
index 37b739c..82ae9ce 100644
--- a/src/beeper_desktop_api/types/message_send_response.py
+++ b/src/beeper_desktop_api/types/message_send_response.py
@@ -11,8 +11,8 @@ class MessageSendResponse(BaseModel):
chat_id: str = FieldInfo(alias="chatID")
"""Chat ID.
- Input routes also accept the local chat ID from this Beeper Desktop installation
- when available.
+ Input routes also accept the local chat ID from this installation when
+ available.
"""
pending_message_id: str = FieldInfo(alias="pendingMessageID")
diff --git a/src/beeper_desktop_api/types/message_update_params.py b/src/beeper_desktop_api/types/message_update_params.py
index 9f62d93..024259b 100644
--- a/src/beeper_desktop_api/types/message_update_params.py
+++ b/src/beeper_desktop_api/types/message_update_params.py
@@ -13,8 +13,8 @@ class MessageUpdateParams(TypedDict, total=False):
chat_id: Required[Annotated[str, PropertyInfo(alias="chatID")]]
"""Chat ID.
- Input routes also accept the local chat ID from this Beeper Desktop installation
- when available.
+ Input routes also accept the local chat ID from this installation when
+ available.
"""
text: Required[str]
diff --git a/src/beeper_desktop_api/types/provisioning_capabilities.py b/src/beeper_desktop_api/types/provisioning_capabilities.py
new file mode 100644
index 0000000..2a6d15a
--- /dev/null
+++ b/src/beeper_desktop_api/types/provisioning_capabilities.py
@@ -0,0 +1,20 @@
+# File generated from our OpenAPI spec by Stainless. See CONTRIBUTING.md for details.
+
+from typing import Dict, Optional
+
+from .._models import BaseModel
+from .group_type_capabilities import GroupTypeCapabilities
+from .resolve_identifier_capabilities import ResolveIdentifierCapabilities
+
+__all__ = ["ProvisioningCapabilities"]
+
+
+class ProvisioningCapabilities(BaseModel):
+ """Advanced network capabilities for account lookup and group creation."""
+
+ group_creation: Dict[str, GroupTypeCapabilities]
+
+ resolve_identifier: ResolveIdentifierCapabilities
+ """Identifier lookup capabilities for this bridge."""
+
+ image_pack_import: Optional[bool] = None
diff --git a/src/beeper_desktop_api/types/resolve_identifier_capabilities.py b/src/beeper_desktop_api/types/resolve_identifier_capabilities.py
new file mode 100644
index 0000000..23038a6
--- /dev/null
+++ b/src/beeper_desktop_api/types/resolve_identifier_capabilities.py
@@ -0,0 +1,23 @@
+# File generated from our OpenAPI spec by Stainless. See CONTRIBUTING.md for details.
+
+from .._models import BaseModel
+
+__all__ = ["ResolveIdentifierCapabilities"]
+
+
+class ResolveIdentifierCapabilities(BaseModel):
+ """Identifier lookup capabilities for this bridge."""
+
+ any_phone: bool
+
+ contact_list: bool
+
+ create_dm: bool
+
+ lookup_email: bool
+
+ lookup_phone: bool
+
+ lookup_username: bool
+
+ search: bool
diff --git a/src/beeper_desktop_api/types/shared/__init__.py b/src/beeper_desktop_api/types/shared/__init__.py
index cb669ed..c29fad6 100644
--- a/src/beeper_desktop_api/types/shared/__init__.py
+++ b/src/beeper_desktop_api/types/shared/__init__.py
@@ -4,4 +4,6 @@
from .error import Error as Error
from .message import Message as Message
from .reaction import Reaction as Reaction
+from .api_error import APIError as APIError
from .attachment import Attachment as Attachment
+from .app_state_snapshot import AppStateSnapshot as AppStateSnapshot
diff --git a/src/beeper_desktop_api/types/shared/api_error.py b/src/beeper_desktop_api/types/shared/api_error.py
new file mode 100644
index 0000000..1f000f0
--- /dev/null
+++ b/src/beeper_desktop_api/types/shared/api_error.py
@@ -0,0 +1,15 @@
+# File generated from our OpenAPI spec by Stainless. See CONTRIBUTING.md for details.
+
+from typing import Dict, Optional
+
+from ..._models import BaseModel
+
+__all__ = ["APIError"]
+
+
+class APIError(BaseModel):
+ code: str
+
+ message: str
+
+ details: Optional[Dict[str, Optional[object]]] = None
diff --git a/src/beeper_desktop_api/types/shared/app_state_snapshot.py b/src/beeper_desktop_api/types/shared/app_state_snapshot.py
new file mode 100644
index 0000000..58eea37
--- /dev/null
+++ b/src/beeper_desktop_api/types/shared/app_state_snapshot.py
@@ -0,0 +1,184 @@
+# File generated from our OpenAPI spec by Stainless. See CONTRIBUTING.md for details.
+
+from typing import List, Optional
+from typing_extensions import Literal
+
+from pydantic import Field as FieldInfo
+
+from ..._models import BaseModel
+
+__all__ = [
+ "AppStateSnapshot",
+ "E2EE",
+ "E2EESecrets",
+ "Matrix",
+ "Verification",
+ "VerificationError",
+ "VerificationOtherDevice",
+ "VerificationQr",
+ "VerificationSAS",
+]
+
+
+class E2EESecrets(BaseModel):
+ """Encrypted messaging keys available on this device."""
+
+ master_key: bool = FieldInfo(alias="masterKey")
+ """Whether the account identity key is available."""
+
+ megolm_backup_key: bool = FieldInfo(alias="megolmBackupKey")
+ """Whether the encrypted message backup key is available."""
+
+ recovery_key: bool = FieldInfo(alias="recoveryKey")
+ """Whether a recovery key is available."""
+
+ self_signing_key: bool = FieldInfo(alias="selfSigningKey")
+ """Whether the device trust key is available."""
+
+ user_signing_key: bool = FieldInfo(alias="userSigningKey")
+ """Whether the user trust key is available."""
+
+
+class E2EE(BaseModel):
+ """Encrypted messaging setup status."""
+
+ cross_signing: bool = FieldInfo(alias="crossSigning")
+ """Whether this account can verify trusted devices."""
+
+ first_sync_done: bool = FieldInfo(alias="firstSyncDone")
+ """Whether the first encrypted message sync is complete."""
+
+ has_backed_up_recovery_key: bool = FieldInfo(alias="hasBackedUpRecoveryKey")
+ """Whether the user confirmed that they saved their recovery key."""
+
+ initialized: bool
+ """Whether encrypted messaging setup has started."""
+
+ key_backup: bool = FieldInfo(alias="keyBackup")
+ """Whether encrypted message backup is available."""
+
+ secrets: E2EESecrets
+ """Encrypted messaging keys available on this device."""
+
+ secret_storage: bool = FieldInfo(alias="secretStorage")
+ """Whether secure key storage is available."""
+
+ verified: bool
+ """Whether this device is trusted for encrypted messages."""
+
+ recovery_key_generated_at: Optional[float] = FieldInfo(alias="recoveryKeyGeneratedAt", default=None)
+ """Unix timestamp for when the recovery key was created."""
+
+
+class Matrix(BaseModel):
+ """Signed-in account details. Omitted until sign-in is complete."""
+
+ device_id: str = FieldInfo(alias="deviceID")
+ """Current device ID."""
+
+ homeserver: str
+ """Beeper homeserver URL for this account."""
+
+ user_id: str = FieldInfo(alias="userID")
+ """Signed-in Beeper user ID."""
+
+
+class VerificationError(BaseModel):
+ """Verification error details, if verification stopped."""
+
+ code: str
+ """Verification error code."""
+
+ reason: str
+ """User-facing verification error message."""
+
+
+class VerificationOtherDevice(BaseModel):
+ """Other device participating in verification."""
+
+ id: str
+ """Other device ID."""
+
+ name: Optional[str] = None
+ """Other device display name, if known."""
+
+
+class VerificationQr(BaseModel):
+ """QR verification data."""
+
+ data: str
+ """QR code payload to display for verification."""
+
+
+class VerificationSAS(BaseModel):
+ """Emoji or number comparison data for verification."""
+
+ emojis: str
+ """Emoji sequence to compare on both devices."""
+
+ decimals: Optional[str] = None
+ """Number sequence to compare on both devices."""
+
+
+class Verification(BaseModel):
+ """Trusted device verification progress."""
+
+ id: str
+ """Verification ID to pass in verification action paths."""
+
+ available_actions: List[Literal["accept", "cancel", "qr.confirmScanned", "sas.start", "sas.confirm"]] = FieldInfo(
+ alias="availableActions"
+ )
+ """Verification actions that are valid for the current state."""
+
+ direction: Literal["incoming", "outgoing"]
+ """Whether this device started or received the verification."""
+
+ methods: List[Literal["qr", "sas"]]
+ """Verification methods supported for this transaction."""
+
+ purpose: Literal["login", "device"]
+ """Why this verification exists."""
+
+ state: Literal["requested", "ready", "sas_ready", "qr_scanned", "done", "cancelled", "error"]
+ """Current trusted-device verification state."""
+
+ error: Optional[VerificationError] = None
+ """Verification error details, if verification stopped."""
+
+ other_device: Optional[VerificationOtherDevice] = FieldInfo(alias="otherDevice", default=None)
+ """Other device participating in verification."""
+
+ other_user_id: Optional[str] = FieldInfo(alias="otherUserID", default=None)
+ """Other Beeper user participating in verification."""
+
+ qr: Optional[VerificationQr] = None
+ """QR verification data."""
+
+ sas: Optional[VerificationSAS] = None
+ """Emoji or number comparison data for verification."""
+
+
+class AppStateSnapshot(BaseModel):
+ e2ee: E2EE
+ """Encrypted messaging setup status."""
+
+ state: Literal[
+ "needs-login",
+ "initializing",
+ "needs-cross-signing-setup",
+ "needs-verification",
+ "needs-secrets",
+ "needs-first-sync",
+ "ready",
+ ]
+ """
+ Current sign-in and encrypted messaging setup state for Beeper Desktop or Beeper
+ Server.
+ """
+
+ matrix: Optional[Matrix] = None
+ """Signed-in account details. Omitted until sign-in is complete."""
+
+ verification: Optional[Verification] = None
+ """Trusted device verification progress."""
diff --git a/src/beeper_desktop_api/types/shared/attachment.py b/src/beeper_desktop_api/types/shared/attachment.py
index 1ec39f6..cd7ed3b 100644
--- a/src/beeper_desktop_api/types/shared/attachment.py
+++ b/src/beeper_desktop_api/types/shared/attachment.py
@@ -36,7 +36,7 @@ class Attachment(BaseModel):
"""Attachment type."""
id: Optional[str] = None
- """Attachment identifier (typically an mxc:// URL).
+ """Attachment identifier, typically an mxc:// URL.
Use the download file endpoint to get a local file path.
"""
@@ -65,7 +65,7 @@ class Attachment(BaseModel):
poster_img: Optional[str] = FieldInfo(alias="posterImg", default=None)
"""Preview image URL for video attachments (poster frame).
- May be temporary or local-only to this device; download promptly if durable
+ May be temporary or available only on this device; download promptly if durable
access is needed.
"""
@@ -75,7 +75,7 @@ class Attachment(BaseModel):
src_url: Optional[str] = FieldInfo(alias="srcURL", default=None)
"""Public URL or local file path to fetch the file.
- May be temporary or local-only to this device; download promptly if durable
+ May be temporary or available only on this device; download promptly if durable
access is needed.
"""
diff --git a/src/beeper_desktop_api/types/shared/message.py b/src/beeper_desktop_api/types/shared/message.py
index 40206de..2f74fe4 100644
--- a/src/beeper_desktop_api/types/shared/message.py
+++ b/src/beeper_desktop_api/types/shared/message.py
@@ -33,14 +33,14 @@ class Link(BaseModel):
favicon: Optional[str] = None
"""Favicon URL if available.
- May be temporary or local-only to this device; download promptly if durable
+ May be temporary or available only on this device; download promptly if durable
access is needed.
"""
img: Optional[str] = None
"""Preview image URL if available.
- May be temporary or local-only to this device; download promptly if durable
+ May be temporary or available only on this device; download promptly if durable
access is needed.
"""
@@ -67,7 +67,10 @@ class SendStatus(BaseModel):
"""User IDs the message was delivered to, when reported by the network."""
internal_error: Optional[str] = FieldInfo(alias="internalError", default=None)
- """Internal bridge error detail. Intended for diagnostics, not end-user display."""
+ """Diagnostic error detail from the messaging network adapter.
+
+ Do not show directly to users.
+ """
message: Optional[str] = None
"""Human-readable send status or failure message."""
@@ -86,14 +89,14 @@ class Message(BaseModel):
chat_id: str = FieldInfo(alias="chatID")
"""Chat ID.
- Input routes also accept the local chat ID from this Beeper Desktop installation
- when available.
+ Input routes also accept the local chat ID from this installation when
+ available.
"""
sender_id: str = FieldInfo(alias="senderID")
- """
- Matrix-style fully-qualified sender user ID, usually including a bridge prefix
- and homeserver.
+ """Fully qualified sender user ID.
+
+ Network-backed IDs usually include the network prefix and homeserver.
"""
sort_key: str = FieldInfo(alias="sortKey")
@@ -139,15 +142,13 @@ class Message(BaseModel):
"""Read receipt state for this message, when available."""
sender_name: Optional[str] = FieldInfo(alias="senderName", default=None)
- """
- Resolved sender display name (impersonator/full name/username/participant name).
- """
+ """Resolved sender display name."""
send_status: Optional[SendStatus] = FieldInfo(alias="sendStatus", default=None)
"""Message send status for this message, when reported by the bridge."""
text: Optional[str] = None
- """Matrix HTML body if present."""
+ """Rich-text message body if present."""
type: Optional[
Literal["TEXT", "NOTICE", "IMAGE", "VIDEO", "VOICE", "AUDIO", "FILE", "STICKER", "LOCATION", "REACTION"]
diff --git a/src/beeper_desktop_api/types/shared/reaction.py b/src/beeper_desktop_api/types/shared/reaction.py
index e28fb69..751db4e 100644
--- a/src/beeper_desktop_api/types/shared/reaction.py
+++ b/src/beeper_desktop_api/types/shared/reaction.py
@@ -32,6 +32,6 @@ class Reaction(BaseModel):
img_url: Optional[str] = FieldInfo(alias="imgURL", default=None)
"""URL to the reaction's image.
- May be temporary or local-only to this device; download promptly if durable
+ May be temporary or available only on this device; download promptly if durable
access is needed.
"""
diff --git a/src/beeper_desktop_api/types/shared/user.py b/src/beeper_desktop_api/types/shared/user.py
index f77a31f..2f170ab 100644
--- a/src/beeper_desktop_api/types/shared/user.py
+++ b/src/beeper_desktop_api/types/shared/user.py
@@ -30,9 +30,9 @@ class User(BaseModel):
img_url: Optional[str] = FieldInfo(alias="imgURL", default=None)
"""Avatar image URL if available.
- This may be a remote URL, Matrix media URL, data URL, or local filesystem URL
- depending on source and endpoint. May be temporary or local-only to this device;
- download promptly if durable access is needed.
+ This may be a remote URL, media URL, data URL, or local file URL depending on
+ the source. May be temporary or available only on this device; download promptly
+ if durable access is needed.
"""
is_self: Optional[bool] = FieldInfo(alias="isSelf", default=None)
diff --git a/tests/api_resources/app/__init__.py b/tests/api_resources/app/__init__.py
new file mode 100644
index 0000000..fd8019a
--- /dev/null
+++ b/tests/api_resources/app/__init__.py
@@ -0,0 +1 @@
+# File generated from our OpenAPI spec by Stainless. See CONTRIBUTING.md for details.
diff --git a/tests/api_resources/app/login/__init__.py b/tests/api_resources/app/login/__init__.py
new file mode 100644
index 0000000..fd8019a
--- /dev/null
+++ b/tests/api_resources/app/login/__init__.py
@@ -0,0 +1 @@
+# File generated from our OpenAPI spec by Stainless. See CONTRIBUTING.md for details.
diff --git a/tests/api_resources/app/login/verification/__init__.py b/tests/api_resources/app/login/verification/__init__.py
new file mode 100644
index 0000000..fd8019a
--- /dev/null
+++ b/tests/api_resources/app/login/verification/__init__.py
@@ -0,0 +1 @@
+# File generated from our OpenAPI spec by Stainless. See CONTRIBUTING.md for details.
diff --git a/tests/api_resources/app/login/verification/recovery_key/__init__.py b/tests/api_resources/app/login/verification/recovery_key/__init__.py
new file mode 100644
index 0000000..fd8019a
--- /dev/null
+++ b/tests/api_resources/app/login/verification/recovery_key/__init__.py
@@ -0,0 +1 @@
+# File generated from our OpenAPI spec by Stainless. See CONTRIBUTING.md for details.
diff --git a/tests/api_resources/app/login/verification/recovery_key/test_reset.py b/tests/api_resources/app/login/verification/recovery_key/test_reset.py
new file mode 100644
index 0000000..9b96cbd
--- /dev/null
+++ b/tests/api_resources/app/login/verification/recovery_key/test_reset.py
@@ -0,0 +1,153 @@
+# File generated from our OpenAPI spec by Stainless. See CONTRIBUTING.md for details.
+
+from __future__ import annotations
+
+import os
+from typing import Any, cast
+
+import pytest
+
+from tests.utils import assert_matches_type
+from beeper_desktop_api import BeeperDesktop, AsyncBeeperDesktop
+from beeper_desktop_api.types.app.login.verification.recovery_key import (
+ ResetCreateResponse,
+ ResetConfirmResponse,
+)
+
+base_url = os.environ.get("TEST_API_BASE_URL", "http://127.0.0.1:4010")
+
+
+class TestReset:
+ parametrize = pytest.mark.parametrize("client", [False, True], indirect=True, ids=["loose", "strict"])
+
+ @parametrize
+ def test_method_create(self, client: BeeperDesktop) -> None:
+ reset = client.app.login.verification.recovery_key.reset.create()
+ assert_matches_type(ResetCreateResponse, reset, path=["response"])
+
+ @parametrize
+ def test_method_create_with_all_params(self, client: BeeperDesktop) -> None:
+ reset = client.app.login.verification.recovery_key.reset.create(
+ existing_recovery_key="existingRecoveryKey",
+ )
+ assert_matches_type(ResetCreateResponse, reset, path=["response"])
+
+ @parametrize
+ def test_raw_response_create(self, client: BeeperDesktop) -> None:
+ response = client.app.login.verification.recovery_key.reset.with_raw_response.create()
+
+ assert response.is_closed is True
+ assert response.http_request.headers.get("X-Stainless-Lang") == "python"
+ reset = response.parse()
+ assert_matches_type(ResetCreateResponse, reset, path=["response"])
+
+ @parametrize
+ def test_streaming_response_create(self, client: BeeperDesktop) -> None:
+ with client.app.login.verification.recovery_key.reset.with_streaming_response.create() as response:
+ assert not response.is_closed
+ assert response.http_request.headers.get("X-Stainless-Lang") == "python"
+
+ reset = response.parse()
+ assert_matches_type(ResetCreateResponse, reset, path=["response"])
+
+ assert cast(Any, response.is_closed) is True
+
+ @parametrize
+ def test_method_confirm(self, client: BeeperDesktop) -> None:
+ reset = client.app.login.verification.recovery_key.reset.confirm(
+ recovery_key="x",
+ )
+ assert_matches_type(ResetConfirmResponse, reset, path=["response"])
+
+ @parametrize
+ def test_raw_response_confirm(self, client: BeeperDesktop) -> None:
+ response = client.app.login.verification.recovery_key.reset.with_raw_response.confirm(
+ recovery_key="x",
+ )
+
+ assert response.is_closed is True
+ assert response.http_request.headers.get("X-Stainless-Lang") == "python"
+ reset = response.parse()
+ assert_matches_type(ResetConfirmResponse, reset, path=["response"])
+
+ @parametrize
+ def test_streaming_response_confirm(self, client: BeeperDesktop) -> None:
+ with client.app.login.verification.recovery_key.reset.with_streaming_response.confirm(
+ recovery_key="x",
+ ) as response:
+ assert not response.is_closed
+ assert response.http_request.headers.get("X-Stainless-Lang") == "python"
+
+ reset = response.parse()
+ assert_matches_type(ResetConfirmResponse, reset, path=["response"])
+
+ assert cast(Any, response.is_closed) is True
+
+
+class TestAsyncReset:
+ parametrize = pytest.mark.parametrize(
+ "async_client", [False, True, {"http_client": "aiohttp"}], indirect=True, ids=["loose", "strict", "aiohttp"]
+ )
+
+ @parametrize
+ async def test_method_create(self, async_client: AsyncBeeperDesktop) -> None:
+ reset = await async_client.app.login.verification.recovery_key.reset.create()
+ assert_matches_type(ResetCreateResponse, reset, path=["response"])
+
+ @parametrize
+ async def test_method_create_with_all_params(self, async_client: AsyncBeeperDesktop) -> None:
+ reset = await async_client.app.login.verification.recovery_key.reset.create(
+ existing_recovery_key="existingRecoveryKey",
+ )
+ assert_matches_type(ResetCreateResponse, reset, path=["response"])
+
+ @parametrize
+ async def test_raw_response_create(self, async_client: AsyncBeeperDesktop) -> None:
+ response = await async_client.app.login.verification.recovery_key.reset.with_raw_response.create()
+
+ assert response.is_closed is True
+ assert response.http_request.headers.get("X-Stainless-Lang") == "python"
+ reset = await response.parse()
+ assert_matches_type(ResetCreateResponse, reset, path=["response"])
+
+ @parametrize
+ async def test_streaming_response_create(self, async_client: AsyncBeeperDesktop) -> None:
+ async with async_client.app.login.verification.recovery_key.reset.with_streaming_response.create() as response:
+ assert not response.is_closed
+ assert response.http_request.headers.get("X-Stainless-Lang") == "python"
+
+ reset = await response.parse()
+ assert_matches_type(ResetCreateResponse, reset, path=["response"])
+
+ assert cast(Any, response.is_closed) is True
+
+ @parametrize
+ async def test_method_confirm(self, async_client: AsyncBeeperDesktop) -> None:
+ reset = await async_client.app.login.verification.recovery_key.reset.confirm(
+ recovery_key="x",
+ )
+ assert_matches_type(ResetConfirmResponse, reset, path=["response"])
+
+ @parametrize
+ async def test_raw_response_confirm(self, async_client: AsyncBeeperDesktop) -> None:
+ response = await async_client.app.login.verification.recovery_key.reset.with_raw_response.confirm(
+ recovery_key="x",
+ )
+
+ assert response.is_closed is True
+ assert response.http_request.headers.get("X-Stainless-Lang") == "python"
+ reset = await response.parse()
+ assert_matches_type(ResetConfirmResponse, reset, path=["response"])
+
+ @parametrize
+ async def test_streaming_response_confirm(self, async_client: AsyncBeeperDesktop) -> None:
+ async with async_client.app.login.verification.recovery_key.reset.with_streaming_response.confirm(
+ recovery_key="x",
+ ) as response:
+ assert not response.is_closed
+ assert response.http_request.headers.get("X-Stainless-Lang") == "python"
+
+ reset = await response.parse()
+ assert_matches_type(ResetConfirmResponse, reset, path=["response"])
+
+ assert cast(Any, response.is_closed) is True
diff --git a/tests/api_resources/app/login/verification/test_recovery_key.py b/tests/api_resources/app/login/verification/test_recovery_key.py
new file mode 100644
index 0000000..5778b2a
--- /dev/null
+++ b/tests/api_resources/app/login/verification/test_recovery_key.py
@@ -0,0 +1,86 @@
+# File generated from our OpenAPI spec by Stainless. See CONTRIBUTING.md for details.
+
+from __future__ import annotations
+
+import os
+from typing import Any, cast
+
+import pytest
+
+from tests.utils import assert_matches_type
+from beeper_desktop_api import BeeperDesktop, AsyncBeeperDesktop
+from beeper_desktop_api.types.app.login.verification import RecoveryKeyVerifyResponse
+
+base_url = os.environ.get("TEST_API_BASE_URL", "http://127.0.0.1:4010")
+
+
+class TestRecoveryKey:
+ parametrize = pytest.mark.parametrize("client", [False, True], indirect=True, ids=["loose", "strict"])
+
+ @parametrize
+ def test_method_verify(self, client: BeeperDesktop) -> None:
+ recovery_key = client.app.login.verification.recovery_key.verify(
+ recovery_key="x",
+ )
+ assert_matches_type(RecoveryKeyVerifyResponse, recovery_key, path=["response"])
+
+ @parametrize
+ def test_raw_response_verify(self, client: BeeperDesktop) -> None:
+ response = client.app.login.verification.recovery_key.with_raw_response.verify(
+ recovery_key="x",
+ )
+
+ assert response.is_closed is True
+ assert response.http_request.headers.get("X-Stainless-Lang") == "python"
+ recovery_key = response.parse()
+ assert_matches_type(RecoveryKeyVerifyResponse, recovery_key, path=["response"])
+
+ @parametrize
+ def test_streaming_response_verify(self, client: BeeperDesktop) -> None:
+ with client.app.login.verification.recovery_key.with_streaming_response.verify(
+ recovery_key="x",
+ ) as response:
+ assert not response.is_closed
+ assert response.http_request.headers.get("X-Stainless-Lang") == "python"
+
+ recovery_key = response.parse()
+ assert_matches_type(RecoveryKeyVerifyResponse, recovery_key, path=["response"])
+
+ assert cast(Any, response.is_closed) is True
+
+
+class TestAsyncRecoveryKey:
+ parametrize = pytest.mark.parametrize(
+ "async_client", [False, True, {"http_client": "aiohttp"}], indirect=True, ids=["loose", "strict", "aiohttp"]
+ )
+
+ @parametrize
+ async def test_method_verify(self, async_client: AsyncBeeperDesktop) -> None:
+ recovery_key = await async_client.app.login.verification.recovery_key.verify(
+ recovery_key="x",
+ )
+ assert_matches_type(RecoveryKeyVerifyResponse, recovery_key, path=["response"])
+
+ @parametrize
+ async def test_raw_response_verify(self, async_client: AsyncBeeperDesktop) -> None:
+ response = await async_client.app.login.verification.recovery_key.with_raw_response.verify(
+ recovery_key="x",
+ )
+
+ assert response.is_closed is True
+ assert response.http_request.headers.get("X-Stainless-Lang") == "python"
+ recovery_key = await response.parse()
+ assert_matches_type(RecoveryKeyVerifyResponse, recovery_key, path=["response"])
+
+ @parametrize
+ async def test_streaming_response_verify(self, async_client: AsyncBeeperDesktop) -> None:
+ async with async_client.app.login.verification.recovery_key.with_streaming_response.verify(
+ recovery_key="x",
+ ) as response:
+ assert not response.is_closed
+ assert response.http_request.headers.get("X-Stainless-Lang") == "python"
+
+ recovery_key = await response.parse()
+ assert_matches_type(RecoveryKeyVerifyResponse, recovery_key, path=["response"])
+
+ assert cast(Any, response.is_closed) is True
diff --git a/tests/api_resources/app/test_login.py b/tests/api_resources/app/test_login.py
new file mode 100644
index 0000000..343ef54
--- /dev/null
+++ b/tests/api_resources/app/test_login.py
@@ -0,0 +1,294 @@
+# File generated from our OpenAPI spec by Stainless. See CONTRIBUTING.md for details.
+
+from __future__ import annotations
+
+import os
+from typing import Any, cast
+
+import pytest
+
+from tests.utils import assert_matches_type
+from beeper_desktop_api import BeeperDesktop, AsyncBeeperDesktop
+from beeper_desktop_api.types.app import (
+ LoginStartResponse,
+ LoginRegisterResponse,
+ LoginResponseResponse,
+)
+
+base_url = os.environ.get("TEST_API_BASE_URL", "http://127.0.0.1:4010")
+
+
+class TestLogin:
+ parametrize = pytest.mark.parametrize("client", [False, True], indirect=True, ids=["loose", "strict"])
+
+ @parametrize
+ def test_method_email(self, client: BeeperDesktop) -> None:
+ login = client.app.login.email(
+ email="dev@stainless.com",
+ setup_request_id="setupRequestID",
+ )
+ assert login is None
+
+ @parametrize
+ def test_raw_response_email(self, client: BeeperDesktop) -> None:
+ response = client.app.login.with_raw_response.email(
+ email="dev@stainless.com",
+ setup_request_id="setupRequestID",
+ )
+
+ assert response.is_closed is True
+ assert response.http_request.headers.get("X-Stainless-Lang") == "python"
+ login = response.parse()
+ assert login is None
+
+ @parametrize
+ def test_streaming_response_email(self, client: BeeperDesktop) -> None:
+ with client.app.login.with_streaming_response.email(
+ email="dev@stainless.com",
+ setup_request_id="setupRequestID",
+ ) as response:
+ assert not response.is_closed
+ assert response.http_request.headers.get("X-Stainless-Lang") == "python"
+
+ login = response.parse()
+ assert login is None
+
+ assert cast(Any, response.is_closed) is True
+
+ @parametrize
+ def test_method_register(self, client: BeeperDesktop) -> None:
+ login = client.app.login.register(
+ accept_terms=True,
+ lead_token="leadToken",
+ setup_request_id="setupRequestID",
+ username="x",
+ )
+ assert_matches_type(LoginRegisterResponse, login, path=["response"])
+
+ @parametrize
+ def test_raw_response_register(self, client: BeeperDesktop) -> None:
+ response = client.app.login.with_raw_response.register(
+ accept_terms=True,
+ lead_token="leadToken",
+ setup_request_id="setupRequestID",
+ username="x",
+ )
+
+ assert response.is_closed is True
+ assert response.http_request.headers.get("X-Stainless-Lang") == "python"
+ login = response.parse()
+ assert_matches_type(LoginRegisterResponse, login, path=["response"])
+
+ @parametrize
+ def test_streaming_response_register(self, client: BeeperDesktop) -> None:
+ with client.app.login.with_streaming_response.register(
+ accept_terms=True,
+ lead_token="leadToken",
+ setup_request_id="setupRequestID",
+ username="x",
+ ) as response:
+ assert not response.is_closed
+ assert response.http_request.headers.get("X-Stainless-Lang") == "python"
+
+ login = response.parse()
+ assert_matches_type(LoginRegisterResponse, login, path=["response"])
+
+ assert cast(Any, response.is_closed) is True
+
+ @parametrize
+ def test_method_response(self, client: BeeperDesktop) -> None:
+ login = client.app.login.response(
+ response="response",
+ setup_request_id="setupRequestID",
+ )
+ assert_matches_type(LoginResponseResponse, login, path=["response"])
+
+ @parametrize
+ def test_raw_response_response(self, client: BeeperDesktop) -> None:
+ response = client.app.login.with_raw_response.response(
+ response="response",
+ setup_request_id="setupRequestID",
+ )
+
+ assert response.is_closed is True
+ assert response.http_request.headers.get("X-Stainless-Lang") == "python"
+ login = response.parse()
+ assert_matches_type(LoginResponseResponse, login, path=["response"])
+
+ @parametrize
+ def test_streaming_response_response(self, client: BeeperDesktop) -> None:
+ with client.app.login.with_streaming_response.response(
+ response="response",
+ setup_request_id="setupRequestID",
+ ) as response:
+ assert not response.is_closed
+ assert response.http_request.headers.get("X-Stainless-Lang") == "python"
+
+ login = response.parse()
+ assert_matches_type(LoginResponseResponse, login, path=["response"])
+
+ assert cast(Any, response.is_closed) is True
+
+ @parametrize
+ def test_method_start(self, client: BeeperDesktop) -> None:
+ login = client.app.login.start()
+ assert_matches_type(LoginStartResponse, login, path=["response"])
+
+ @parametrize
+ def test_raw_response_start(self, client: BeeperDesktop) -> None:
+ response = client.app.login.with_raw_response.start()
+
+ assert response.is_closed is True
+ assert response.http_request.headers.get("X-Stainless-Lang") == "python"
+ login = response.parse()
+ assert_matches_type(LoginStartResponse, login, path=["response"])
+
+ @parametrize
+ def test_streaming_response_start(self, client: BeeperDesktop) -> None:
+ with client.app.login.with_streaming_response.start() as response:
+ assert not response.is_closed
+ assert response.http_request.headers.get("X-Stainless-Lang") == "python"
+
+ login = response.parse()
+ assert_matches_type(LoginStartResponse, login, path=["response"])
+
+ assert cast(Any, response.is_closed) is True
+
+
+class TestAsyncLogin:
+ parametrize = pytest.mark.parametrize(
+ "async_client", [False, True, {"http_client": "aiohttp"}], indirect=True, ids=["loose", "strict", "aiohttp"]
+ )
+
+ @parametrize
+ async def test_method_email(self, async_client: AsyncBeeperDesktop) -> None:
+ login = await async_client.app.login.email(
+ email="dev@stainless.com",
+ setup_request_id="setupRequestID",
+ )
+ assert login is None
+
+ @parametrize
+ async def test_raw_response_email(self, async_client: AsyncBeeperDesktop) -> None:
+ response = await async_client.app.login.with_raw_response.email(
+ email="dev@stainless.com",
+ setup_request_id="setupRequestID",
+ )
+
+ assert response.is_closed is True
+ assert response.http_request.headers.get("X-Stainless-Lang") == "python"
+ login = await response.parse()
+ assert login is None
+
+ @parametrize
+ async def test_streaming_response_email(self, async_client: AsyncBeeperDesktop) -> None:
+ async with async_client.app.login.with_streaming_response.email(
+ email="dev@stainless.com",
+ setup_request_id="setupRequestID",
+ ) as response:
+ assert not response.is_closed
+ assert response.http_request.headers.get("X-Stainless-Lang") == "python"
+
+ login = await response.parse()
+ assert login is None
+
+ assert cast(Any, response.is_closed) is True
+
+ @parametrize
+ async def test_method_register(self, async_client: AsyncBeeperDesktop) -> None:
+ login = await async_client.app.login.register(
+ accept_terms=True,
+ lead_token="leadToken",
+ setup_request_id="setupRequestID",
+ username="x",
+ )
+ assert_matches_type(LoginRegisterResponse, login, path=["response"])
+
+ @parametrize
+ async def test_raw_response_register(self, async_client: AsyncBeeperDesktop) -> None:
+ response = await async_client.app.login.with_raw_response.register(
+ accept_terms=True,
+ lead_token="leadToken",
+ setup_request_id="setupRequestID",
+ username="x",
+ )
+
+ assert response.is_closed is True
+ assert response.http_request.headers.get("X-Stainless-Lang") == "python"
+ login = await response.parse()
+ assert_matches_type(LoginRegisterResponse, login, path=["response"])
+
+ @parametrize
+ async def test_streaming_response_register(self, async_client: AsyncBeeperDesktop) -> None:
+ async with async_client.app.login.with_streaming_response.register(
+ accept_terms=True,
+ lead_token="leadToken",
+ setup_request_id="setupRequestID",
+ username="x",
+ ) as response:
+ assert not response.is_closed
+ assert response.http_request.headers.get("X-Stainless-Lang") == "python"
+
+ login = await response.parse()
+ assert_matches_type(LoginRegisterResponse, login, path=["response"])
+
+ assert cast(Any, response.is_closed) is True
+
+ @parametrize
+ async def test_method_response(self, async_client: AsyncBeeperDesktop) -> None:
+ login = await async_client.app.login.response(
+ response="response",
+ setup_request_id="setupRequestID",
+ )
+ assert_matches_type(LoginResponseResponse, login, path=["response"])
+
+ @parametrize
+ async def test_raw_response_response(self, async_client: AsyncBeeperDesktop) -> None:
+ response = await async_client.app.login.with_raw_response.response(
+ response="response",
+ setup_request_id="setupRequestID",
+ )
+
+ assert response.is_closed is True
+ assert response.http_request.headers.get("X-Stainless-Lang") == "python"
+ login = await response.parse()
+ assert_matches_type(LoginResponseResponse, login, path=["response"])
+
+ @parametrize
+ async def test_streaming_response_response(self, async_client: AsyncBeeperDesktop) -> None:
+ async with async_client.app.login.with_streaming_response.response(
+ response="response",
+ setup_request_id="setupRequestID",
+ ) as response:
+ assert not response.is_closed
+ assert response.http_request.headers.get("X-Stainless-Lang") == "python"
+
+ login = await response.parse()
+ assert_matches_type(LoginResponseResponse, login, path=["response"])
+
+ assert cast(Any, response.is_closed) is True
+
+ @parametrize
+ async def test_method_start(self, async_client: AsyncBeeperDesktop) -> None:
+ login = await async_client.app.login.start()
+ assert_matches_type(LoginStartResponse, login, path=["response"])
+
+ @parametrize
+ async def test_raw_response_start(self, async_client: AsyncBeeperDesktop) -> None:
+ response = await async_client.app.login.with_raw_response.start()
+
+ assert response.is_closed is True
+ assert response.http_request.headers.get("X-Stainless-Lang") == "python"
+ login = await response.parse()
+ assert_matches_type(LoginStartResponse, login, path=["response"])
+
+ @parametrize
+ async def test_streaming_response_start(self, async_client: AsyncBeeperDesktop) -> None:
+ async with async_client.app.login.with_streaming_response.start() as response:
+ assert not response.is_closed
+ assert response.http_request.headers.get("X-Stainless-Lang") == "python"
+
+ login = await response.parse()
+ assert_matches_type(LoginStartResponse, login, path=["response"])
+
+ assert cast(Any, response.is_closed) is True
diff --git a/tests/api_resources/app/test_verifications.py b/tests/api_resources/app/test_verifications.py
new file mode 100644
index 0000000..b94df48
--- /dev/null
+++ b/tests/api_resources/app/test_verifications.py
@@ -0,0 +1,392 @@
+# File generated from our OpenAPI spec by Stainless. See CONTRIBUTING.md for details.
+
+from __future__ import annotations
+
+import os
+from typing import Any, cast
+
+import pytest
+
+from tests.utils import assert_matches_type
+from beeper_desktop_api import BeeperDesktop, AsyncBeeperDesktop
+from beeper_desktop_api.types.app import (
+ VerificationListResponse,
+ VerificationAcceptResponse,
+ VerificationCancelResponse,
+ VerificationCreateResponse,
+ VerificationRetrieveResponse,
+)
+
+base_url = os.environ.get("TEST_API_BASE_URL", "http://127.0.0.1:4010")
+
+
+class TestVerifications:
+ parametrize = pytest.mark.parametrize("client", [False, True], indirect=True, ids=["loose", "strict"])
+
+ @parametrize
+ def test_method_create(self, client: BeeperDesktop) -> None:
+ verification = client.app.verifications.create()
+ assert_matches_type(VerificationCreateResponse, verification, path=["response"])
+
+ @parametrize
+ def test_method_create_with_all_params(self, client: BeeperDesktop) -> None:
+ verification = client.app.verifications.create(
+ purpose="login",
+ user_id="userID",
+ )
+ assert_matches_type(VerificationCreateResponse, verification, path=["response"])
+
+ @parametrize
+ def test_raw_response_create(self, client: BeeperDesktop) -> None:
+ response = client.app.verifications.with_raw_response.create()
+
+ assert response.is_closed is True
+ assert response.http_request.headers.get("X-Stainless-Lang") == "python"
+ verification = response.parse()
+ assert_matches_type(VerificationCreateResponse, verification, path=["response"])
+
+ @parametrize
+ def test_streaming_response_create(self, client: BeeperDesktop) -> None:
+ with client.app.verifications.with_streaming_response.create() as response:
+ assert not response.is_closed
+ assert response.http_request.headers.get("X-Stainless-Lang") == "python"
+
+ verification = response.parse()
+ assert_matches_type(VerificationCreateResponse, verification, path=["response"])
+
+ assert cast(Any, response.is_closed) is True
+
+ @parametrize
+ def test_method_retrieve(self, client: BeeperDesktop) -> None:
+ verification = client.app.verifications.retrieve(
+ "x",
+ )
+ assert_matches_type(VerificationRetrieveResponse, verification, path=["response"])
+
+ @parametrize
+ def test_raw_response_retrieve(self, client: BeeperDesktop) -> None:
+ response = client.app.verifications.with_raw_response.retrieve(
+ "x",
+ )
+
+ assert response.is_closed is True
+ assert response.http_request.headers.get("X-Stainless-Lang") == "python"
+ verification = response.parse()
+ assert_matches_type(VerificationRetrieveResponse, verification, path=["response"])
+
+ @parametrize
+ def test_streaming_response_retrieve(self, client: BeeperDesktop) -> None:
+ with client.app.verifications.with_streaming_response.retrieve(
+ "x",
+ ) as response:
+ assert not response.is_closed
+ assert response.http_request.headers.get("X-Stainless-Lang") == "python"
+
+ verification = response.parse()
+ assert_matches_type(VerificationRetrieveResponse, verification, path=["response"])
+
+ assert cast(Any, response.is_closed) is True
+
+ @parametrize
+ def test_path_params_retrieve(self, client: BeeperDesktop) -> None:
+ with pytest.raises(ValueError, match=r"Expected a non-empty value for `verification_id` but received ''"):
+ client.app.verifications.with_raw_response.retrieve(
+ "",
+ )
+
+ @parametrize
+ def test_method_list(self, client: BeeperDesktop) -> None:
+ verification = client.app.verifications.list()
+ assert_matches_type(VerificationListResponse, verification, path=["response"])
+
+ @parametrize
+ def test_raw_response_list(self, client: BeeperDesktop) -> None:
+ response = client.app.verifications.with_raw_response.list()
+
+ assert response.is_closed is True
+ assert response.http_request.headers.get("X-Stainless-Lang") == "python"
+ verification = response.parse()
+ assert_matches_type(VerificationListResponse, verification, path=["response"])
+
+ @parametrize
+ def test_streaming_response_list(self, client: BeeperDesktop) -> None:
+ with client.app.verifications.with_streaming_response.list() as response:
+ assert not response.is_closed
+ assert response.http_request.headers.get("X-Stainless-Lang") == "python"
+
+ verification = response.parse()
+ assert_matches_type(VerificationListResponse, verification, path=["response"])
+
+ assert cast(Any, response.is_closed) is True
+
+ @parametrize
+ def test_method_accept(self, client: BeeperDesktop) -> None:
+ verification = client.app.verifications.accept(
+ "x",
+ )
+ assert_matches_type(VerificationAcceptResponse, verification, path=["response"])
+
+ @parametrize
+ def test_raw_response_accept(self, client: BeeperDesktop) -> None:
+ response = client.app.verifications.with_raw_response.accept(
+ "x",
+ )
+
+ assert response.is_closed is True
+ assert response.http_request.headers.get("X-Stainless-Lang") == "python"
+ verification = response.parse()
+ assert_matches_type(VerificationAcceptResponse, verification, path=["response"])
+
+ @parametrize
+ def test_streaming_response_accept(self, client: BeeperDesktop) -> None:
+ with client.app.verifications.with_streaming_response.accept(
+ "x",
+ ) as response:
+ assert not response.is_closed
+ assert response.http_request.headers.get("X-Stainless-Lang") == "python"
+
+ verification = response.parse()
+ assert_matches_type(VerificationAcceptResponse, verification, path=["response"])
+
+ assert cast(Any, response.is_closed) is True
+
+ @parametrize
+ def test_path_params_accept(self, client: BeeperDesktop) -> None:
+ with pytest.raises(ValueError, match=r"Expected a non-empty value for `verification_id` but received ''"):
+ client.app.verifications.with_raw_response.accept(
+ "",
+ )
+
+ @parametrize
+ def test_method_cancel(self, client: BeeperDesktop) -> None:
+ verification = client.app.verifications.cancel(
+ verification_id="x",
+ )
+ assert_matches_type(VerificationCancelResponse, verification, path=["response"])
+
+ @parametrize
+ def test_method_cancel_with_all_params(self, client: BeeperDesktop) -> None:
+ verification = client.app.verifications.cancel(
+ verification_id="x",
+ code="code",
+ reason="reason",
+ )
+ assert_matches_type(VerificationCancelResponse, verification, path=["response"])
+
+ @parametrize
+ def test_raw_response_cancel(self, client: BeeperDesktop) -> None:
+ response = client.app.verifications.with_raw_response.cancel(
+ verification_id="x",
+ )
+
+ assert response.is_closed is True
+ assert response.http_request.headers.get("X-Stainless-Lang") == "python"
+ verification = response.parse()
+ assert_matches_type(VerificationCancelResponse, verification, path=["response"])
+
+ @parametrize
+ def test_streaming_response_cancel(self, client: BeeperDesktop) -> None:
+ with client.app.verifications.with_streaming_response.cancel(
+ verification_id="x",
+ ) as response:
+ assert not response.is_closed
+ assert response.http_request.headers.get("X-Stainless-Lang") == "python"
+
+ verification = response.parse()
+ assert_matches_type(VerificationCancelResponse, verification, path=["response"])
+
+ assert cast(Any, response.is_closed) is True
+
+ @parametrize
+ def test_path_params_cancel(self, client: BeeperDesktop) -> None:
+ with pytest.raises(ValueError, match=r"Expected a non-empty value for `verification_id` but received ''"):
+ client.app.verifications.with_raw_response.cancel(
+ verification_id="",
+ )
+
+
+class TestAsyncVerifications:
+ parametrize = pytest.mark.parametrize(
+ "async_client", [False, True, {"http_client": "aiohttp"}], indirect=True, ids=["loose", "strict", "aiohttp"]
+ )
+
+ @parametrize
+ async def test_method_create(self, async_client: AsyncBeeperDesktop) -> None:
+ verification = await async_client.app.verifications.create()
+ assert_matches_type(VerificationCreateResponse, verification, path=["response"])
+
+ @parametrize
+ async def test_method_create_with_all_params(self, async_client: AsyncBeeperDesktop) -> None:
+ verification = await async_client.app.verifications.create(
+ purpose="login",
+ user_id="userID",
+ )
+ assert_matches_type(VerificationCreateResponse, verification, path=["response"])
+
+ @parametrize
+ async def test_raw_response_create(self, async_client: AsyncBeeperDesktop) -> None:
+ response = await async_client.app.verifications.with_raw_response.create()
+
+ assert response.is_closed is True
+ assert response.http_request.headers.get("X-Stainless-Lang") == "python"
+ verification = await response.parse()
+ assert_matches_type(VerificationCreateResponse, verification, path=["response"])
+
+ @parametrize
+ async def test_streaming_response_create(self, async_client: AsyncBeeperDesktop) -> None:
+ async with async_client.app.verifications.with_streaming_response.create() as response:
+ assert not response.is_closed
+ assert response.http_request.headers.get("X-Stainless-Lang") == "python"
+
+ verification = await response.parse()
+ assert_matches_type(VerificationCreateResponse, verification, path=["response"])
+
+ assert cast(Any, response.is_closed) is True
+
+ @parametrize
+ async def test_method_retrieve(self, async_client: AsyncBeeperDesktop) -> None:
+ verification = await async_client.app.verifications.retrieve(
+ "x",
+ )
+ assert_matches_type(VerificationRetrieveResponse, verification, path=["response"])
+
+ @parametrize
+ async def test_raw_response_retrieve(self, async_client: AsyncBeeperDesktop) -> None:
+ response = await async_client.app.verifications.with_raw_response.retrieve(
+ "x",
+ )
+
+ assert response.is_closed is True
+ assert response.http_request.headers.get("X-Stainless-Lang") == "python"
+ verification = await response.parse()
+ assert_matches_type(VerificationRetrieveResponse, verification, path=["response"])
+
+ @parametrize
+ async def test_streaming_response_retrieve(self, async_client: AsyncBeeperDesktop) -> None:
+ async with async_client.app.verifications.with_streaming_response.retrieve(
+ "x",
+ ) as response:
+ assert not response.is_closed
+ assert response.http_request.headers.get("X-Stainless-Lang") == "python"
+
+ verification = await response.parse()
+ assert_matches_type(VerificationRetrieveResponse, verification, path=["response"])
+
+ assert cast(Any, response.is_closed) is True
+
+ @parametrize
+ async def test_path_params_retrieve(self, async_client: AsyncBeeperDesktop) -> None:
+ with pytest.raises(ValueError, match=r"Expected a non-empty value for `verification_id` but received ''"):
+ await async_client.app.verifications.with_raw_response.retrieve(
+ "",
+ )
+
+ @parametrize
+ async def test_method_list(self, async_client: AsyncBeeperDesktop) -> None:
+ verification = await async_client.app.verifications.list()
+ assert_matches_type(VerificationListResponse, verification, path=["response"])
+
+ @parametrize
+ async def test_raw_response_list(self, async_client: AsyncBeeperDesktop) -> None:
+ response = await async_client.app.verifications.with_raw_response.list()
+
+ assert response.is_closed is True
+ assert response.http_request.headers.get("X-Stainless-Lang") == "python"
+ verification = await response.parse()
+ assert_matches_type(VerificationListResponse, verification, path=["response"])
+
+ @parametrize
+ async def test_streaming_response_list(self, async_client: AsyncBeeperDesktop) -> None:
+ async with async_client.app.verifications.with_streaming_response.list() as response:
+ assert not response.is_closed
+ assert response.http_request.headers.get("X-Stainless-Lang") == "python"
+
+ verification = await response.parse()
+ assert_matches_type(VerificationListResponse, verification, path=["response"])
+
+ assert cast(Any, response.is_closed) is True
+
+ @parametrize
+ async def test_method_accept(self, async_client: AsyncBeeperDesktop) -> None:
+ verification = await async_client.app.verifications.accept(
+ "x",
+ )
+ assert_matches_type(VerificationAcceptResponse, verification, path=["response"])
+
+ @parametrize
+ async def test_raw_response_accept(self, async_client: AsyncBeeperDesktop) -> None:
+ response = await async_client.app.verifications.with_raw_response.accept(
+ "x",
+ )
+
+ assert response.is_closed is True
+ assert response.http_request.headers.get("X-Stainless-Lang") == "python"
+ verification = await response.parse()
+ assert_matches_type(VerificationAcceptResponse, verification, path=["response"])
+
+ @parametrize
+ async def test_streaming_response_accept(self, async_client: AsyncBeeperDesktop) -> None:
+ async with async_client.app.verifications.with_streaming_response.accept(
+ "x",
+ ) as response:
+ assert not response.is_closed
+ assert response.http_request.headers.get("X-Stainless-Lang") == "python"
+
+ verification = await response.parse()
+ assert_matches_type(VerificationAcceptResponse, verification, path=["response"])
+
+ assert cast(Any, response.is_closed) is True
+
+ @parametrize
+ async def test_path_params_accept(self, async_client: AsyncBeeperDesktop) -> None:
+ with pytest.raises(ValueError, match=r"Expected a non-empty value for `verification_id` but received ''"):
+ await async_client.app.verifications.with_raw_response.accept(
+ "",
+ )
+
+ @parametrize
+ async def test_method_cancel(self, async_client: AsyncBeeperDesktop) -> None:
+ verification = await async_client.app.verifications.cancel(
+ verification_id="x",
+ )
+ assert_matches_type(VerificationCancelResponse, verification, path=["response"])
+
+ @parametrize
+ async def test_method_cancel_with_all_params(self, async_client: AsyncBeeperDesktop) -> None:
+ verification = await async_client.app.verifications.cancel(
+ verification_id="x",
+ code="code",
+ reason="reason",
+ )
+ assert_matches_type(VerificationCancelResponse, verification, path=["response"])
+
+ @parametrize
+ async def test_raw_response_cancel(self, async_client: AsyncBeeperDesktop) -> None:
+ response = await async_client.app.verifications.with_raw_response.cancel(
+ verification_id="x",
+ )
+
+ assert response.is_closed is True
+ assert response.http_request.headers.get("X-Stainless-Lang") == "python"
+ verification = await response.parse()
+ assert_matches_type(VerificationCancelResponse, verification, path=["response"])
+
+ @parametrize
+ async def test_streaming_response_cancel(self, async_client: AsyncBeeperDesktop) -> None:
+ async with async_client.app.verifications.with_streaming_response.cancel(
+ verification_id="x",
+ ) as response:
+ assert not response.is_closed
+ assert response.http_request.headers.get("X-Stainless-Lang") == "python"
+
+ verification = await response.parse()
+ assert_matches_type(VerificationCancelResponse, verification, path=["response"])
+
+ assert cast(Any, response.is_closed) is True
+
+ @parametrize
+ async def test_path_params_cancel(self, async_client: AsyncBeeperDesktop) -> None:
+ with pytest.raises(ValueError, match=r"Expected a non-empty value for `verification_id` but received ''"):
+ await async_client.app.verifications.with_raw_response.cancel(
+ verification_id="",
+ )
diff --git a/tests/api_resources/app/verifications/__init__.py b/tests/api_resources/app/verifications/__init__.py
new file mode 100644
index 0000000..fd8019a
--- /dev/null
+++ b/tests/api_resources/app/verifications/__init__.py
@@ -0,0 +1 @@
+# File generated from our OpenAPI spec by Stainless. See CONTRIBUTING.md for details.
diff --git a/tests/api_resources/app/verifications/test_qr.py b/tests/api_resources/app/verifications/test_qr.py
new file mode 100644
index 0000000..2678078
--- /dev/null
+++ b/tests/api_resources/app/verifications/test_qr.py
@@ -0,0 +1,162 @@
+# File generated from our OpenAPI spec by Stainless. See CONTRIBUTING.md for details.
+
+from __future__ import annotations
+
+import os
+from typing import Any, cast
+
+import pytest
+
+from tests.utils import assert_matches_type
+from beeper_desktop_api import BeeperDesktop, AsyncBeeperDesktop
+from beeper_desktop_api.types.app.verifications import QrScanResponse, QrConfirmScannedResponse
+
+base_url = os.environ.get("TEST_API_BASE_URL", "http://127.0.0.1:4010")
+
+
+class TestQr:
+ parametrize = pytest.mark.parametrize("client", [False, True], indirect=True, ids=["loose", "strict"])
+
+ @parametrize
+ def test_method_confirm_scanned(self, client: BeeperDesktop) -> None:
+ qr = client.app.verifications.qr.confirm_scanned(
+ "x",
+ )
+ assert_matches_type(QrConfirmScannedResponse, qr, path=["response"])
+
+ @parametrize
+ def test_raw_response_confirm_scanned(self, client: BeeperDesktop) -> None:
+ response = client.app.verifications.qr.with_raw_response.confirm_scanned(
+ "x",
+ )
+
+ assert response.is_closed is True
+ assert response.http_request.headers.get("X-Stainless-Lang") == "python"
+ qr = response.parse()
+ assert_matches_type(QrConfirmScannedResponse, qr, path=["response"])
+
+ @parametrize
+ def test_streaming_response_confirm_scanned(self, client: BeeperDesktop) -> None:
+ with client.app.verifications.qr.with_streaming_response.confirm_scanned(
+ "x",
+ ) as response:
+ assert not response.is_closed
+ assert response.http_request.headers.get("X-Stainless-Lang") == "python"
+
+ qr = response.parse()
+ assert_matches_type(QrConfirmScannedResponse, qr, path=["response"])
+
+ assert cast(Any, response.is_closed) is True
+
+ @parametrize
+ def test_path_params_confirm_scanned(self, client: BeeperDesktop) -> None:
+ with pytest.raises(ValueError, match=r"Expected a non-empty value for `verification_id` but received ''"):
+ client.app.verifications.qr.with_raw_response.confirm_scanned(
+ "",
+ )
+
+ @parametrize
+ def test_method_scan(self, client: BeeperDesktop) -> None:
+ qr = client.app.verifications.qr.scan(
+ data="x",
+ )
+ assert_matches_type(QrScanResponse, qr, path=["response"])
+
+ @parametrize
+ def test_raw_response_scan(self, client: BeeperDesktop) -> None:
+ response = client.app.verifications.qr.with_raw_response.scan(
+ data="x",
+ )
+
+ assert response.is_closed is True
+ assert response.http_request.headers.get("X-Stainless-Lang") == "python"
+ qr = response.parse()
+ assert_matches_type(QrScanResponse, qr, path=["response"])
+
+ @parametrize
+ def test_streaming_response_scan(self, client: BeeperDesktop) -> None:
+ with client.app.verifications.qr.with_streaming_response.scan(
+ data="x",
+ ) as response:
+ assert not response.is_closed
+ assert response.http_request.headers.get("X-Stainless-Lang") == "python"
+
+ qr = response.parse()
+ assert_matches_type(QrScanResponse, qr, path=["response"])
+
+ assert cast(Any, response.is_closed) is True
+
+
+class TestAsyncQr:
+ parametrize = pytest.mark.parametrize(
+ "async_client", [False, True, {"http_client": "aiohttp"}], indirect=True, ids=["loose", "strict", "aiohttp"]
+ )
+
+ @parametrize
+ async def test_method_confirm_scanned(self, async_client: AsyncBeeperDesktop) -> None:
+ qr = await async_client.app.verifications.qr.confirm_scanned(
+ "x",
+ )
+ assert_matches_type(QrConfirmScannedResponse, qr, path=["response"])
+
+ @parametrize
+ async def test_raw_response_confirm_scanned(self, async_client: AsyncBeeperDesktop) -> None:
+ response = await async_client.app.verifications.qr.with_raw_response.confirm_scanned(
+ "x",
+ )
+
+ assert response.is_closed is True
+ assert response.http_request.headers.get("X-Stainless-Lang") == "python"
+ qr = await response.parse()
+ assert_matches_type(QrConfirmScannedResponse, qr, path=["response"])
+
+ @parametrize
+ async def test_streaming_response_confirm_scanned(self, async_client: AsyncBeeperDesktop) -> None:
+ async with async_client.app.verifications.qr.with_streaming_response.confirm_scanned(
+ "x",
+ ) as response:
+ assert not response.is_closed
+ assert response.http_request.headers.get("X-Stainless-Lang") == "python"
+
+ qr = await response.parse()
+ assert_matches_type(QrConfirmScannedResponse, qr, path=["response"])
+
+ assert cast(Any, response.is_closed) is True
+
+ @parametrize
+ async def test_path_params_confirm_scanned(self, async_client: AsyncBeeperDesktop) -> None:
+ with pytest.raises(ValueError, match=r"Expected a non-empty value for `verification_id` but received ''"):
+ await async_client.app.verifications.qr.with_raw_response.confirm_scanned(
+ "",
+ )
+
+ @parametrize
+ async def test_method_scan(self, async_client: AsyncBeeperDesktop) -> None:
+ qr = await async_client.app.verifications.qr.scan(
+ data="x",
+ )
+ assert_matches_type(QrScanResponse, qr, path=["response"])
+
+ @parametrize
+ async def test_raw_response_scan(self, async_client: AsyncBeeperDesktop) -> None:
+ response = await async_client.app.verifications.qr.with_raw_response.scan(
+ data="x",
+ )
+
+ assert response.is_closed is True
+ assert response.http_request.headers.get("X-Stainless-Lang") == "python"
+ qr = await response.parse()
+ assert_matches_type(QrScanResponse, qr, path=["response"])
+
+ @parametrize
+ async def test_streaming_response_scan(self, async_client: AsyncBeeperDesktop) -> None:
+ async with async_client.app.verifications.qr.with_streaming_response.scan(
+ data="x",
+ ) as response:
+ assert not response.is_closed
+ assert response.http_request.headers.get("X-Stainless-Lang") == "python"
+
+ qr = await response.parse()
+ assert_matches_type(QrScanResponse, qr, path=["response"])
+
+ assert cast(Any, response.is_closed) is True
diff --git a/tests/api_resources/app/verifications/test_sas.py b/tests/api_resources/app/verifications/test_sas.py
new file mode 100644
index 0000000..020880b
--- /dev/null
+++ b/tests/api_resources/app/verifications/test_sas.py
@@ -0,0 +1,176 @@
+# File generated from our OpenAPI spec by Stainless. See CONTRIBUTING.md for details.
+
+from __future__ import annotations
+
+import os
+from typing import Any, cast
+
+import pytest
+
+from tests.utils import assert_matches_type
+from beeper_desktop_api import BeeperDesktop, AsyncBeeperDesktop
+from beeper_desktop_api.types.app.verifications import SASStartResponse, SASConfirmResponse
+
+base_url = os.environ.get("TEST_API_BASE_URL", "http://127.0.0.1:4010")
+
+
+class TestSAS:
+ parametrize = pytest.mark.parametrize("client", [False, True], indirect=True, ids=["loose", "strict"])
+
+ @parametrize
+ def test_method_confirm(self, client: BeeperDesktop) -> None:
+ sas = client.app.verifications.sas.confirm(
+ "x",
+ )
+ assert_matches_type(SASConfirmResponse, sas, path=["response"])
+
+ @parametrize
+ def test_raw_response_confirm(self, client: BeeperDesktop) -> None:
+ response = client.app.verifications.sas.with_raw_response.confirm(
+ "x",
+ )
+
+ assert response.is_closed is True
+ assert response.http_request.headers.get("X-Stainless-Lang") == "python"
+ sas = response.parse()
+ assert_matches_type(SASConfirmResponse, sas, path=["response"])
+
+ @parametrize
+ def test_streaming_response_confirm(self, client: BeeperDesktop) -> None:
+ with client.app.verifications.sas.with_streaming_response.confirm(
+ "x",
+ ) as response:
+ assert not response.is_closed
+ assert response.http_request.headers.get("X-Stainless-Lang") == "python"
+
+ sas = response.parse()
+ assert_matches_type(SASConfirmResponse, sas, path=["response"])
+
+ assert cast(Any, response.is_closed) is True
+
+ @parametrize
+ def test_path_params_confirm(self, client: BeeperDesktop) -> None:
+ with pytest.raises(ValueError, match=r"Expected a non-empty value for `verification_id` but received ''"):
+ client.app.verifications.sas.with_raw_response.confirm(
+ "",
+ )
+
+ @parametrize
+ def test_method_start(self, client: BeeperDesktop) -> None:
+ sas = client.app.verifications.sas.start(
+ "x",
+ )
+ assert_matches_type(SASStartResponse, sas, path=["response"])
+
+ @parametrize
+ def test_raw_response_start(self, client: BeeperDesktop) -> None:
+ response = client.app.verifications.sas.with_raw_response.start(
+ "x",
+ )
+
+ assert response.is_closed is True
+ assert response.http_request.headers.get("X-Stainless-Lang") == "python"
+ sas = response.parse()
+ assert_matches_type(SASStartResponse, sas, path=["response"])
+
+ @parametrize
+ def test_streaming_response_start(self, client: BeeperDesktop) -> None:
+ with client.app.verifications.sas.with_streaming_response.start(
+ "x",
+ ) as response:
+ assert not response.is_closed
+ assert response.http_request.headers.get("X-Stainless-Lang") == "python"
+
+ sas = response.parse()
+ assert_matches_type(SASStartResponse, sas, path=["response"])
+
+ assert cast(Any, response.is_closed) is True
+
+ @parametrize
+ def test_path_params_start(self, client: BeeperDesktop) -> None:
+ with pytest.raises(ValueError, match=r"Expected a non-empty value for `verification_id` but received ''"):
+ client.app.verifications.sas.with_raw_response.start(
+ "",
+ )
+
+
+class TestAsyncSAS:
+ parametrize = pytest.mark.parametrize(
+ "async_client", [False, True, {"http_client": "aiohttp"}], indirect=True, ids=["loose", "strict", "aiohttp"]
+ )
+
+ @parametrize
+ async def test_method_confirm(self, async_client: AsyncBeeperDesktop) -> None:
+ sas = await async_client.app.verifications.sas.confirm(
+ "x",
+ )
+ assert_matches_type(SASConfirmResponse, sas, path=["response"])
+
+ @parametrize
+ async def test_raw_response_confirm(self, async_client: AsyncBeeperDesktop) -> None:
+ response = await async_client.app.verifications.sas.with_raw_response.confirm(
+ "x",
+ )
+
+ assert response.is_closed is True
+ assert response.http_request.headers.get("X-Stainless-Lang") == "python"
+ sas = await response.parse()
+ assert_matches_type(SASConfirmResponse, sas, path=["response"])
+
+ @parametrize
+ async def test_streaming_response_confirm(self, async_client: AsyncBeeperDesktop) -> None:
+ async with async_client.app.verifications.sas.with_streaming_response.confirm(
+ "x",
+ ) as response:
+ assert not response.is_closed
+ assert response.http_request.headers.get("X-Stainless-Lang") == "python"
+
+ sas = await response.parse()
+ assert_matches_type(SASConfirmResponse, sas, path=["response"])
+
+ assert cast(Any, response.is_closed) is True
+
+ @parametrize
+ async def test_path_params_confirm(self, async_client: AsyncBeeperDesktop) -> None:
+ with pytest.raises(ValueError, match=r"Expected a non-empty value for `verification_id` but received ''"):
+ await async_client.app.verifications.sas.with_raw_response.confirm(
+ "",
+ )
+
+ @parametrize
+ async def test_method_start(self, async_client: AsyncBeeperDesktop) -> None:
+ sas = await async_client.app.verifications.sas.start(
+ "x",
+ )
+ assert_matches_type(SASStartResponse, sas, path=["response"])
+
+ @parametrize
+ async def test_raw_response_start(self, async_client: AsyncBeeperDesktop) -> None:
+ response = await async_client.app.verifications.sas.with_raw_response.start(
+ "x",
+ )
+
+ assert response.is_closed is True
+ assert response.http_request.headers.get("X-Stainless-Lang") == "python"
+ sas = await response.parse()
+ assert_matches_type(SASStartResponse, sas, path=["response"])
+
+ @parametrize
+ async def test_streaming_response_start(self, async_client: AsyncBeeperDesktop) -> None:
+ async with async_client.app.verifications.sas.with_streaming_response.start(
+ "x",
+ ) as response:
+ assert not response.is_closed
+ assert response.http_request.headers.get("X-Stainless-Lang") == "python"
+
+ sas = await response.parse()
+ assert_matches_type(SASStartResponse, sas, path=["response"])
+
+ assert cast(Any, response.is_closed) is True
+
+ @parametrize
+ async def test_path_params_start(self, async_client: AsyncBeeperDesktop) -> None:
+ with pytest.raises(ValueError, match=r"Expected a non-empty value for `verification_id` but received ''"):
+ await async_client.app.verifications.sas.with_raw_response.start(
+ "",
+ )
diff --git a/tests/api_resources/bridges/__init__.py b/tests/api_resources/bridges/__init__.py
new file mode 100644
index 0000000..fd8019a
--- /dev/null
+++ b/tests/api_resources/bridges/__init__.py
@@ -0,0 +1 @@
+# File generated from our OpenAPI spec by Stainless. See CONTRIBUTING.md for details.
diff --git a/tests/api_resources/bridges/login_sessions/__init__.py b/tests/api_resources/bridges/login_sessions/__init__.py
new file mode 100644
index 0000000..fd8019a
--- /dev/null
+++ b/tests/api_resources/bridges/login_sessions/__init__.py
@@ -0,0 +1 @@
+# File generated from our OpenAPI spec by Stainless. See CONTRIBUTING.md for details.
diff --git a/tests/api_resources/bridges/login_sessions/test_steps.py b/tests/api_resources/bridges/login_sessions/test_steps.py
new file mode 100644
index 0000000..7b8f9b4
--- /dev/null
+++ b/tests/api_resources/bridges/login_sessions/test_steps.py
@@ -0,0 +1,182 @@
+# File generated from our OpenAPI spec by Stainless. See CONTRIBUTING.md for details.
+
+from __future__ import annotations
+
+import os
+from typing import Any, cast
+
+import pytest
+
+from tests.utils import assert_matches_type
+from beeper_desktop_api import BeeperDesktop, AsyncBeeperDesktop
+from beeper_desktop_api.types import LoginSession
+
+base_url = os.environ.get("TEST_API_BASE_URL", "http://127.0.0.1:4010")
+
+
+class TestSteps:
+ parametrize = pytest.mark.parametrize("client", [False, True], indirect=True, ids=["loose", "strict"])
+
+ @parametrize
+ def test_method_submit(self, client: BeeperDesktop) -> None:
+ step = client.bridges.login_sessions.steps.submit(
+ step_id="x",
+ bridge_id="local-whatsapp",
+ login_session_id="123",
+ type="user_input",
+ )
+ assert_matches_type(LoginSession, step, path=["response"])
+
+ @parametrize
+ def test_method_submit_with_all_params(self, client: BeeperDesktop) -> None:
+ step = client.bridges.login_sessions.steps.submit(
+ step_id="x",
+ bridge_id="local-whatsapp",
+ login_session_id="123",
+ type="user_input",
+ fields={"foo": "string"},
+ last_url="lastURL",
+ source="api",
+ )
+ assert_matches_type(LoginSession, step, path=["response"])
+
+ @parametrize
+ def test_raw_response_submit(self, client: BeeperDesktop) -> None:
+ response = client.bridges.login_sessions.steps.with_raw_response.submit(
+ step_id="x",
+ bridge_id="local-whatsapp",
+ login_session_id="123",
+ type="user_input",
+ )
+
+ assert response.is_closed is True
+ assert response.http_request.headers.get("X-Stainless-Lang") == "python"
+ step = response.parse()
+ assert_matches_type(LoginSession, step, path=["response"])
+
+ @parametrize
+ def test_streaming_response_submit(self, client: BeeperDesktop) -> None:
+ with client.bridges.login_sessions.steps.with_streaming_response.submit(
+ step_id="x",
+ bridge_id="local-whatsapp",
+ login_session_id="123",
+ type="user_input",
+ ) as response:
+ assert not response.is_closed
+ assert response.http_request.headers.get("X-Stainless-Lang") == "python"
+
+ step = response.parse()
+ assert_matches_type(LoginSession, step, path=["response"])
+
+ assert cast(Any, response.is_closed) is True
+
+ @parametrize
+ def test_path_params_submit(self, client: BeeperDesktop) -> None:
+ with pytest.raises(ValueError, match=r"Expected a non-empty value for `bridge_id` but received ''"):
+ client.bridges.login_sessions.steps.with_raw_response.submit(
+ step_id="x",
+ bridge_id="",
+ login_session_id="123",
+ type="user_input",
+ )
+
+ with pytest.raises(ValueError, match=r"Expected a non-empty value for `login_session_id` but received ''"):
+ client.bridges.login_sessions.steps.with_raw_response.submit(
+ step_id="x",
+ bridge_id="local-whatsapp",
+ login_session_id="",
+ type="user_input",
+ )
+
+ with pytest.raises(ValueError, match=r"Expected a non-empty value for `step_id` but received ''"):
+ client.bridges.login_sessions.steps.with_raw_response.submit(
+ step_id="",
+ bridge_id="local-whatsapp",
+ login_session_id="123",
+ type="user_input",
+ )
+
+
+class TestAsyncSteps:
+ parametrize = pytest.mark.parametrize(
+ "async_client", [False, True, {"http_client": "aiohttp"}], indirect=True, ids=["loose", "strict", "aiohttp"]
+ )
+
+ @parametrize
+ async def test_method_submit(self, async_client: AsyncBeeperDesktop) -> None:
+ step = await async_client.bridges.login_sessions.steps.submit(
+ step_id="x",
+ bridge_id="local-whatsapp",
+ login_session_id="123",
+ type="user_input",
+ )
+ assert_matches_type(LoginSession, step, path=["response"])
+
+ @parametrize
+ async def test_method_submit_with_all_params(self, async_client: AsyncBeeperDesktop) -> None:
+ step = await async_client.bridges.login_sessions.steps.submit(
+ step_id="x",
+ bridge_id="local-whatsapp",
+ login_session_id="123",
+ type="user_input",
+ fields={"foo": "string"},
+ last_url="lastURL",
+ source="api",
+ )
+ assert_matches_type(LoginSession, step, path=["response"])
+
+ @parametrize
+ async def test_raw_response_submit(self, async_client: AsyncBeeperDesktop) -> None:
+ response = await async_client.bridges.login_sessions.steps.with_raw_response.submit(
+ step_id="x",
+ bridge_id="local-whatsapp",
+ login_session_id="123",
+ type="user_input",
+ )
+
+ assert response.is_closed is True
+ assert response.http_request.headers.get("X-Stainless-Lang") == "python"
+ step = await response.parse()
+ assert_matches_type(LoginSession, step, path=["response"])
+
+ @parametrize
+ async def test_streaming_response_submit(self, async_client: AsyncBeeperDesktop) -> None:
+ async with async_client.bridges.login_sessions.steps.with_streaming_response.submit(
+ step_id="x",
+ bridge_id="local-whatsapp",
+ login_session_id="123",
+ type="user_input",
+ ) as response:
+ assert not response.is_closed
+ assert response.http_request.headers.get("X-Stainless-Lang") == "python"
+
+ step = await response.parse()
+ assert_matches_type(LoginSession, step, path=["response"])
+
+ assert cast(Any, response.is_closed) is True
+
+ @parametrize
+ async def test_path_params_submit(self, async_client: AsyncBeeperDesktop) -> None:
+ with pytest.raises(ValueError, match=r"Expected a non-empty value for `bridge_id` but received ''"):
+ await async_client.bridges.login_sessions.steps.with_raw_response.submit(
+ step_id="x",
+ bridge_id="",
+ login_session_id="123",
+ type="user_input",
+ )
+
+ with pytest.raises(ValueError, match=r"Expected a non-empty value for `login_session_id` but received ''"):
+ await async_client.bridges.login_sessions.steps.with_raw_response.submit(
+ step_id="x",
+ bridge_id="local-whatsapp",
+ login_session_id="",
+ type="user_input",
+ )
+
+ with pytest.raises(ValueError, match=r"Expected a non-empty value for `step_id` but received ''"):
+ await async_client.bridges.login_sessions.steps.with_raw_response.submit(
+ step_id="",
+ bridge_id="local-whatsapp",
+ login_session_id="123",
+ type="user_input",
+ )
diff --git a/tests/api_resources/bridges/test_login_flows.py b/tests/api_resources/bridges/test_login_flows.py
new file mode 100644
index 0000000..d14a6fb
--- /dev/null
+++ b/tests/api_resources/bridges/test_login_flows.py
@@ -0,0 +1,100 @@
+# File generated from our OpenAPI spec by Stainless. See CONTRIBUTING.md for details.
+
+from __future__ import annotations
+
+import os
+from typing import Any, cast
+
+import pytest
+
+from tests.utils import assert_matches_type
+from beeper_desktop_api import BeeperDesktop, AsyncBeeperDesktop
+from beeper_desktop_api.types.bridges import LoginFlowListResponse
+
+base_url = os.environ.get("TEST_API_BASE_URL", "http://127.0.0.1:4010")
+
+
+class TestLoginFlows:
+ parametrize = pytest.mark.parametrize("client", [False, True], indirect=True, ids=["loose", "strict"])
+
+ @parametrize
+ def test_method_list(self, client: BeeperDesktop) -> None:
+ login_flow = client.bridges.login_flows.list(
+ "local-whatsapp",
+ )
+ assert_matches_type(LoginFlowListResponse, login_flow, path=["response"])
+
+ @parametrize
+ def test_raw_response_list(self, client: BeeperDesktop) -> None:
+ response = client.bridges.login_flows.with_raw_response.list(
+ "local-whatsapp",
+ )
+
+ assert response.is_closed is True
+ assert response.http_request.headers.get("X-Stainless-Lang") == "python"
+ login_flow = response.parse()
+ assert_matches_type(LoginFlowListResponse, login_flow, path=["response"])
+
+ @parametrize
+ def test_streaming_response_list(self, client: BeeperDesktop) -> None:
+ with client.bridges.login_flows.with_streaming_response.list(
+ "local-whatsapp",
+ ) as response:
+ assert not response.is_closed
+ assert response.http_request.headers.get("X-Stainless-Lang") == "python"
+
+ login_flow = response.parse()
+ assert_matches_type(LoginFlowListResponse, login_flow, path=["response"])
+
+ assert cast(Any, response.is_closed) is True
+
+ @parametrize
+ def test_path_params_list(self, client: BeeperDesktop) -> None:
+ with pytest.raises(ValueError, match=r"Expected a non-empty value for `bridge_id` but received ''"):
+ client.bridges.login_flows.with_raw_response.list(
+ "",
+ )
+
+
+class TestAsyncLoginFlows:
+ parametrize = pytest.mark.parametrize(
+ "async_client", [False, True, {"http_client": "aiohttp"}], indirect=True, ids=["loose", "strict", "aiohttp"]
+ )
+
+ @parametrize
+ async def test_method_list(self, async_client: AsyncBeeperDesktop) -> None:
+ login_flow = await async_client.bridges.login_flows.list(
+ "local-whatsapp",
+ )
+ assert_matches_type(LoginFlowListResponse, login_flow, path=["response"])
+
+ @parametrize
+ async def test_raw_response_list(self, async_client: AsyncBeeperDesktop) -> None:
+ response = await async_client.bridges.login_flows.with_raw_response.list(
+ "local-whatsapp",
+ )
+
+ assert response.is_closed is True
+ assert response.http_request.headers.get("X-Stainless-Lang") == "python"
+ login_flow = await response.parse()
+ assert_matches_type(LoginFlowListResponse, login_flow, path=["response"])
+
+ @parametrize
+ async def test_streaming_response_list(self, async_client: AsyncBeeperDesktop) -> None:
+ async with async_client.bridges.login_flows.with_streaming_response.list(
+ "local-whatsapp",
+ ) as response:
+ assert not response.is_closed
+ assert response.http_request.headers.get("X-Stainless-Lang") == "python"
+
+ login_flow = await response.parse()
+ assert_matches_type(LoginFlowListResponse, login_flow, path=["response"])
+
+ assert cast(Any, response.is_closed) is True
+
+ @parametrize
+ async def test_path_params_list(self, async_client: AsyncBeeperDesktop) -> None:
+ with pytest.raises(ValueError, match=r"Expected a non-empty value for `bridge_id` but received ''"):
+ await async_client.bridges.login_flows.with_raw_response.list(
+ "",
+ )
diff --git a/tests/api_resources/bridges/test_login_sessions.py b/tests/api_resources/bridges/test_login_sessions.py
new file mode 100644
index 0000000..adf87f8
--- /dev/null
+++ b/tests/api_resources/bridges/test_login_sessions.py
@@ -0,0 +1,313 @@
+# File generated from our OpenAPI spec by Stainless. See CONTRIBUTING.md for details.
+
+from __future__ import annotations
+
+import os
+from typing import Any, cast
+
+import pytest
+
+from tests.utils import assert_matches_type
+from beeper_desktop_api import BeeperDesktop, AsyncBeeperDesktop
+from beeper_desktop_api.types import LoginSession
+from beeper_desktop_api.types.bridges import LoginSessionCancelResponse
+
+base_url = os.environ.get("TEST_API_BASE_URL", "http://127.0.0.1:4010")
+
+
+class TestLoginSessions:
+ parametrize = pytest.mark.parametrize("client", [False, True], indirect=True, ids=["loose", "strict"])
+
+ @parametrize
+ def test_method_create(self, client: BeeperDesktop) -> None:
+ login_session = client.bridges.login_sessions.create(
+ bridge_id="local-whatsapp",
+ )
+ assert_matches_type(LoginSession, login_session, path=["response"])
+
+ @parametrize
+ def test_method_create_with_all_params(self, client: BeeperDesktop) -> None:
+ login_session = client.bridges.login_sessions.create(
+ bridge_id="local-whatsapp",
+ account_id="x",
+ flow_id="x",
+ login_id="x",
+ )
+ assert_matches_type(LoginSession, login_session, path=["response"])
+
+ @parametrize
+ def test_raw_response_create(self, client: BeeperDesktop) -> None:
+ response = client.bridges.login_sessions.with_raw_response.create(
+ bridge_id="local-whatsapp",
+ )
+
+ assert response.is_closed is True
+ assert response.http_request.headers.get("X-Stainless-Lang") == "python"
+ login_session = response.parse()
+ assert_matches_type(LoginSession, login_session, path=["response"])
+
+ @parametrize
+ def test_streaming_response_create(self, client: BeeperDesktop) -> None:
+ with client.bridges.login_sessions.with_streaming_response.create(
+ bridge_id="local-whatsapp",
+ ) as response:
+ assert not response.is_closed
+ assert response.http_request.headers.get("X-Stainless-Lang") == "python"
+
+ login_session = response.parse()
+ assert_matches_type(LoginSession, login_session, path=["response"])
+
+ assert cast(Any, response.is_closed) is True
+
+ @parametrize
+ def test_path_params_create(self, client: BeeperDesktop) -> None:
+ with pytest.raises(ValueError, match=r"Expected a non-empty value for `bridge_id` but received ''"):
+ client.bridges.login_sessions.with_raw_response.create(
+ bridge_id="",
+ )
+
+ @parametrize
+ def test_method_retrieve(self, client: BeeperDesktop) -> None:
+ login_session = client.bridges.login_sessions.retrieve(
+ login_session_id="123",
+ bridge_id="local-whatsapp",
+ )
+ assert_matches_type(LoginSession, login_session, path=["response"])
+
+ @parametrize
+ def test_raw_response_retrieve(self, client: BeeperDesktop) -> None:
+ response = client.bridges.login_sessions.with_raw_response.retrieve(
+ login_session_id="123",
+ bridge_id="local-whatsapp",
+ )
+
+ assert response.is_closed is True
+ assert response.http_request.headers.get("X-Stainless-Lang") == "python"
+ login_session = response.parse()
+ assert_matches_type(LoginSession, login_session, path=["response"])
+
+ @parametrize
+ def test_streaming_response_retrieve(self, client: BeeperDesktop) -> None:
+ with client.bridges.login_sessions.with_streaming_response.retrieve(
+ login_session_id="123",
+ bridge_id="local-whatsapp",
+ ) as response:
+ assert not response.is_closed
+ assert response.http_request.headers.get("X-Stainless-Lang") == "python"
+
+ login_session = response.parse()
+ assert_matches_type(LoginSession, login_session, path=["response"])
+
+ assert cast(Any, response.is_closed) is True
+
+ @parametrize
+ def test_path_params_retrieve(self, client: BeeperDesktop) -> None:
+ with pytest.raises(ValueError, match=r"Expected a non-empty value for `bridge_id` but received ''"):
+ client.bridges.login_sessions.with_raw_response.retrieve(
+ login_session_id="123",
+ bridge_id="",
+ )
+
+ with pytest.raises(ValueError, match=r"Expected a non-empty value for `login_session_id` but received ''"):
+ client.bridges.login_sessions.with_raw_response.retrieve(
+ login_session_id="",
+ bridge_id="local-whatsapp",
+ )
+
+ @parametrize
+ def test_method_cancel(self, client: BeeperDesktop) -> None:
+ login_session = client.bridges.login_sessions.cancel(
+ login_session_id="123",
+ bridge_id="local-whatsapp",
+ )
+ assert_matches_type(LoginSessionCancelResponse, login_session, path=["response"])
+
+ @parametrize
+ def test_raw_response_cancel(self, client: BeeperDesktop) -> None:
+ response = client.bridges.login_sessions.with_raw_response.cancel(
+ login_session_id="123",
+ bridge_id="local-whatsapp",
+ )
+
+ assert response.is_closed is True
+ assert response.http_request.headers.get("X-Stainless-Lang") == "python"
+ login_session = response.parse()
+ assert_matches_type(LoginSessionCancelResponse, login_session, path=["response"])
+
+ @parametrize
+ def test_streaming_response_cancel(self, client: BeeperDesktop) -> None:
+ with client.bridges.login_sessions.with_streaming_response.cancel(
+ login_session_id="123",
+ bridge_id="local-whatsapp",
+ ) as response:
+ assert not response.is_closed
+ assert response.http_request.headers.get("X-Stainless-Lang") == "python"
+
+ login_session = response.parse()
+ assert_matches_type(LoginSessionCancelResponse, login_session, path=["response"])
+
+ assert cast(Any, response.is_closed) is True
+
+ @parametrize
+ def test_path_params_cancel(self, client: BeeperDesktop) -> None:
+ with pytest.raises(ValueError, match=r"Expected a non-empty value for `bridge_id` but received ''"):
+ client.bridges.login_sessions.with_raw_response.cancel(
+ login_session_id="123",
+ bridge_id="",
+ )
+
+ with pytest.raises(ValueError, match=r"Expected a non-empty value for `login_session_id` but received ''"):
+ client.bridges.login_sessions.with_raw_response.cancel(
+ login_session_id="",
+ bridge_id="local-whatsapp",
+ )
+
+
+class TestAsyncLoginSessions:
+ parametrize = pytest.mark.parametrize(
+ "async_client", [False, True, {"http_client": "aiohttp"}], indirect=True, ids=["loose", "strict", "aiohttp"]
+ )
+
+ @parametrize
+ async def test_method_create(self, async_client: AsyncBeeperDesktop) -> None:
+ login_session = await async_client.bridges.login_sessions.create(
+ bridge_id="local-whatsapp",
+ )
+ assert_matches_type(LoginSession, login_session, path=["response"])
+
+ @parametrize
+ async def test_method_create_with_all_params(self, async_client: AsyncBeeperDesktop) -> None:
+ login_session = await async_client.bridges.login_sessions.create(
+ bridge_id="local-whatsapp",
+ account_id="x",
+ flow_id="x",
+ login_id="x",
+ )
+ assert_matches_type(LoginSession, login_session, path=["response"])
+
+ @parametrize
+ async def test_raw_response_create(self, async_client: AsyncBeeperDesktop) -> None:
+ response = await async_client.bridges.login_sessions.with_raw_response.create(
+ bridge_id="local-whatsapp",
+ )
+
+ assert response.is_closed is True
+ assert response.http_request.headers.get("X-Stainless-Lang") == "python"
+ login_session = await response.parse()
+ assert_matches_type(LoginSession, login_session, path=["response"])
+
+ @parametrize
+ async def test_streaming_response_create(self, async_client: AsyncBeeperDesktop) -> None:
+ async with async_client.bridges.login_sessions.with_streaming_response.create(
+ bridge_id="local-whatsapp",
+ ) as response:
+ assert not response.is_closed
+ assert response.http_request.headers.get("X-Stainless-Lang") == "python"
+
+ login_session = await response.parse()
+ assert_matches_type(LoginSession, login_session, path=["response"])
+
+ assert cast(Any, response.is_closed) is True
+
+ @parametrize
+ async def test_path_params_create(self, async_client: AsyncBeeperDesktop) -> None:
+ with pytest.raises(ValueError, match=r"Expected a non-empty value for `bridge_id` but received ''"):
+ await async_client.bridges.login_sessions.with_raw_response.create(
+ bridge_id="",
+ )
+
+ @parametrize
+ async def test_method_retrieve(self, async_client: AsyncBeeperDesktop) -> None:
+ login_session = await async_client.bridges.login_sessions.retrieve(
+ login_session_id="123",
+ bridge_id="local-whatsapp",
+ )
+ assert_matches_type(LoginSession, login_session, path=["response"])
+
+ @parametrize
+ async def test_raw_response_retrieve(self, async_client: AsyncBeeperDesktop) -> None:
+ response = await async_client.bridges.login_sessions.with_raw_response.retrieve(
+ login_session_id="123",
+ bridge_id="local-whatsapp",
+ )
+
+ assert response.is_closed is True
+ assert response.http_request.headers.get("X-Stainless-Lang") == "python"
+ login_session = await response.parse()
+ assert_matches_type(LoginSession, login_session, path=["response"])
+
+ @parametrize
+ async def test_streaming_response_retrieve(self, async_client: AsyncBeeperDesktop) -> None:
+ async with async_client.bridges.login_sessions.with_streaming_response.retrieve(
+ login_session_id="123",
+ bridge_id="local-whatsapp",
+ ) as response:
+ assert not response.is_closed
+ assert response.http_request.headers.get("X-Stainless-Lang") == "python"
+
+ login_session = await response.parse()
+ assert_matches_type(LoginSession, login_session, path=["response"])
+
+ assert cast(Any, response.is_closed) is True
+
+ @parametrize
+ async def test_path_params_retrieve(self, async_client: AsyncBeeperDesktop) -> None:
+ with pytest.raises(ValueError, match=r"Expected a non-empty value for `bridge_id` but received ''"):
+ await async_client.bridges.login_sessions.with_raw_response.retrieve(
+ login_session_id="123",
+ bridge_id="",
+ )
+
+ with pytest.raises(ValueError, match=r"Expected a non-empty value for `login_session_id` but received ''"):
+ await async_client.bridges.login_sessions.with_raw_response.retrieve(
+ login_session_id="",
+ bridge_id="local-whatsapp",
+ )
+
+ @parametrize
+ async def test_method_cancel(self, async_client: AsyncBeeperDesktop) -> None:
+ login_session = await async_client.bridges.login_sessions.cancel(
+ login_session_id="123",
+ bridge_id="local-whatsapp",
+ )
+ assert_matches_type(LoginSessionCancelResponse, login_session, path=["response"])
+
+ @parametrize
+ async def test_raw_response_cancel(self, async_client: AsyncBeeperDesktop) -> None:
+ response = await async_client.bridges.login_sessions.with_raw_response.cancel(
+ login_session_id="123",
+ bridge_id="local-whatsapp",
+ )
+
+ assert response.is_closed is True
+ assert response.http_request.headers.get("X-Stainless-Lang") == "python"
+ login_session = await response.parse()
+ assert_matches_type(LoginSessionCancelResponse, login_session, path=["response"])
+
+ @parametrize
+ async def test_streaming_response_cancel(self, async_client: AsyncBeeperDesktop) -> None:
+ async with async_client.bridges.login_sessions.with_streaming_response.cancel(
+ login_session_id="123",
+ bridge_id="local-whatsapp",
+ ) as response:
+ assert not response.is_closed
+ assert response.http_request.headers.get("X-Stainless-Lang") == "python"
+
+ login_session = await response.parse()
+ assert_matches_type(LoginSessionCancelResponse, login_session, path=["response"])
+
+ assert cast(Any, response.is_closed) is True
+
+ @parametrize
+ async def test_path_params_cancel(self, async_client: AsyncBeeperDesktop) -> None:
+ with pytest.raises(ValueError, match=r"Expected a non-empty value for `bridge_id` but received ''"):
+ await async_client.bridges.login_sessions.with_raw_response.cancel(
+ login_session_id="123",
+ bridge_id="",
+ )
+
+ with pytest.raises(ValueError, match=r"Expected a non-empty value for `login_session_id` but received ''"):
+ await async_client.bridges.login_sessions.with_raw_response.cancel(
+ login_session_id="",
+ bridge_id="local-whatsapp",
+ )
diff --git a/tests/api_resources/test_accounts.py b/tests/api_resources/test_accounts.py
index 46ac702..dfe06af 100644
--- a/tests/api_resources/test_accounts.py
+++ b/tests/api_resources/test_accounts.py
@@ -9,7 +9,7 @@
from tests.utils import assert_matches_type
from beeper_desktop_api import BeeperDesktop, AsyncBeeperDesktop
-from beeper_desktop_api.types import AccountListResponse
+from beeper_desktop_api.types import AccountListResponse, AccountRetrieveResponse
base_url = os.environ.get("TEST_API_BASE_URL", "http://127.0.0.1:4010")
@@ -17,6 +17,44 @@
class TestAccounts:
parametrize = pytest.mark.parametrize("client", [False, True], indirect=True, ids=["loose", "strict"])
+ @parametrize
+ def test_method_retrieve(self, client: BeeperDesktop) -> None:
+ account = client.accounts.retrieve(
+ "accountID",
+ )
+ assert_matches_type(AccountRetrieveResponse, account, path=["response"])
+
+ @parametrize
+ def test_raw_response_retrieve(self, client: BeeperDesktop) -> None:
+ response = client.accounts.with_raw_response.retrieve(
+ "accountID",
+ )
+
+ assert response.is_closed is True
+ assert response.http_request.headers.get("X-Stainless-Lang") == "python"
+ account = response.parse()
+ assert_matches_type(AccountRetrieveResponse, account, path=["response"])
+
+ @parametrize
+ def test_streaming_response_retrieve(self, client: BeeperDesktop) -> None:
+ with client.accounts.with_streaming_response.retrieve(
+ "accountID",
+ ) as response:
+ assert not response.is_closed
+ assert response.http_request.headers.get("X-Stainless-Lang") == "python"
+
+ account = response.parse()
+ assert_matches_type(AccountRetrieveResponse, account, path=["response"])
+
+ assert cast(Any, response.is_closed) is True
+
+ @parametrize
+ def test_path_params_retrieve(self, client: BeeperDesktop) -> None:
+ with pytest.raises(ValueError, match=r"Expected a non-empty value for `account_id` but received ''"):
+ client.accounts.with_raw_response.retrieve(
+ "",
+ )
+
@parametrize
def test_method_list(self, client: BeeperDesktop) -> None:
account = client.accounts.list()
@@ -48,6 +86,44 @@ class TestAsyncAccounts:
"async_client", [False, True, {"http_client": "aiohttp"}], indirect=True, ids=["loose", "strict", "aiohttp"]
)
+ @parametrize
+ async def test_method_retrieve(self, async_client: AsyncBeeperDesktop) -> None:
+ account = await async_client.accounts.retrieve(
+ "accountID",
+ )
+ assert_matches_type(AccountRetrieveResponse, account, path=["response"])
+
+ @parametrize
+ async def test_raw_response_retrieve(self, async_client: AsyncBeeperDesktop) -> None:
+ response = await async_client.accounts.with_raw_response.retrieve(
+ "accountID",
+ )
+
+ assert response.is_closed is True
+ assert response.http_request.headers.get("X-Stainless-Lang") == "python"
+ account = await response.parse()
+ assert_matches_type(AccountRetrieveResponse, account, path=["response"])
+
+ @parametrize
+ async def test_streaming_response_retrieve(self, async_client: AsyncBeeperDesktop) -> None:
+ async with async_client.accounts.with_streaming_response.retrieve(
+ "accountID",
+ ) as response:
+ assert not response.is_closed
+ assert response.http_request.headers.get("X-Stainless-Lang") == "python"
+
+ account = await response.parse()
+ assert_matches_type(AccountRetrieveResponse, account, path=["response"])
+
+ assert cast(Any, response.is_closed) is True
+
+ @parametrize
+ async def test_path_params_retrieve(self, async_client: AsyncBeeperDesktop) -> None:
+ with pytest.raises(ValueError, match=r"Expected a non-empty value for `account_id` but received ''"):
+ await async_client.accounts.with_raw_response.retrieve(
+ "",
+ )
+
@parametrize
async def test_method_list(self, async_client: AsyncBeeperDesktop) -> None:
account = await async_client.accounts.list()
diff --git a/tests/api_resources/test_app.py b/tests/api_resources/test_app.py
new file mode 100644
index 0000000..5fe739f
--- /dev/null
+++ b/tests/api_resources/test_app.py
@@ -0,0 +1,74 @@
+# File generated from our OpenAPI spec by Stainless. See CONTRIBUTING.md for details.
+
+from __future__ import annotations
+
+import os
+from typing import Any, cast
+
+import pytest
+
+from tests.utils import assert_matches_type
+from beeper_desktop_api import BeeperDesktop, AsyncBeeperDesktop
+from beeper_desktop_api.types import AppSessionResponse
+
+base_url = os.environ.get("TEST_API_BASE_URL", "http://127.0.0.1:4010")
+
+
+class TestApp:
+ parametrize = pytest.mark.parametrize("client", [False, True], indirect=True, ids=["loose", "strict"])
+
+ @parametrize
+ def test_method_session(self, client: BeeperDesktop) -> None:
+ app = client.app.session()
+ assert_matches_type(AppSessionResponse, app, path=["response"])
+
+ @parametrize
+ def test_raw_response_session(self, client: BeeperDesktop) -> None:
+ response = client.app.with_raw_response.session()
+
+ assert response.is_closed is True
+ assert response.http_request.headers.get("X-Stainless-Lang") == "python"
+ app = response.parse()
+ assert_matches_type(AppSessionResponse, app, path=["response"])
+
+ @parametrize
+ def test_streaming_response_session(self, client: BeeperDesktop) -> None:
+ with client.app.with_streaming_response.session() as response:
+ assert not response.is_closed
+ assert response.http_request.headers.get("X-Stainless-Lang") == "python"
+
+ app = response.parse()
+ assert_matches_type(AppSessionResponse, app, path=["response"])
+
+ assert cast(Any, response.is_closed) is True
+
+
+class TestAsyncApp:
+ parametrize = pytest.mark.parametrize(
+ "async_client", [False, True, {"http_client": "aiohttp"}], indirect=True, ids=["loose", "strict", "aiohttp"]
+ )
+
+ @parametrize
+ async def test_method_session(self, async_client: AsyncBeeperDesktop) -> None:
+ app = await async_client.app.session()
+ assert_matches_type(AppSessionResponse, app, path=["response"])
+
+ @parametrize
+ async def test_raw_response_session(self, async_client: AsyncBeeperDesktop) -> None:
+ response = await async_client.app.with_raw_response.session()
+
+ assert response.is_closed is True
+ assert response.http_request.headers.get("X-Stainless-Lang") == "python"
+ app = await response.parse()
+ assert_matches_type(AppSessionResponse, app, path=["response"])
+
+ @parametrize
+ async def test_streaming_response_session(self, async_client: AsyncBeeperDesktop) -> None:
+ async with async_client.app.with_streaming_response.session() as response:
+ assert not response.is_closed
+ assert response.http_request.headers.get("X-Stainless-Lang") == "python"
+
+ app = await response.parse()
+ assert_matches_type(AppSessionResponse, app, path=["response"])
+
+ assert cast(Any, response.is_closed) is True
diff --git a/tests/api_resources/test_bridges.py b/tests/api_resources/test_bridges.py
new file mode 100644
index 0000000..91654f1
--- /dev/null
+++ b/tests/api_resources/test_bridges.py
@@ -0,0 +1,226 @@
+# File generated from our OpenAPI spec by Stainless. See CONTRIBUTING.md for details.
+
+from __future__ import annotations
+
+import os
+from typing import Any, cast
+
+import pytest
+
+from tests.utils import assert_matches_type
+from beeper_desktop_api import BeeperDesktop, AsyncBeeperDesktop
+from beeper_desktop_api.types import BridgeListResponse, BridgeRetrieveResponse, ProvisioningCapabilities
+
+base_url = os.environ.get("TEST_API_BASE_URL", "http://127.0.0.1:4010")
+
+
+class TestBridges:
+ parametrize = pytest.mark.parametrize("client", [False, True], indirect=True, ids=["loose", "strict"])
+
+ @parametrize
+ def test_method_retrieve(self, client: BeeperDesktop) -> None:
+ bridge = client.bridges.retrieve(
+ "local-whatsapp",
+ )
+ assert_matches_type(BridgeRetrieveResponse, bridge, path=["response"])
+
+ @parametrize
+ def test_raw_response_retrieve(self, client: BeeperDesktop) -> None:
+ response = client.bridges.with_raw_response.retrieve(
+ "local-whatsapp",
+ )
+
+ assert response.is_closed is True
+ assert response.http_request.headers.get("X-Stainless-Lang") == "python"
+ bridge = response.parse()
+ assert_matches_type(BridgeRetrieveResponse, bridge, path=["response"])
+
+ @parametrize
+ def test_streaming_response_retrieve(self, client: BeeperDesktop) -> None:
+ with client.bridges.with_streaming_response.retrieve(
+ "local-whatsapp",
+ ) as response:
+ assert not response.is_closed
+ assert response.http_request.headers.get("X-Stainless-Lang") == "python"
+
+ bridge = response.parse()
+ assert_matches_type(BridgeRetrieveResponse, bridge, path=["response"])
+
+ assert cast(Any, response.is_closed) is True
+
+ @parametrize
+ def test_path_params_retrieve(self, client: BeeperDesktop) -> None:
+ with pytest.raises(ValueError, match=r"Expected a non-empty value for `bridge_id` but received ''"):
+ client.bridges.with_raw_response.retrieve(
+ "",
+ )
+
+ @parametrize
+ def test_method_list(self, client: BeeperDesktop) -> None:
+ bridge = client.bridges.list()
+ assert_matches_type(BridgeListResponse, bridge, path=["response"])
+
+ @parametrize
+ def test_raw_response_list(self, client: BeeperDesktop) -> None:
+ response = client.bridges.with_raw_response.list()
+
+ assert response.is_closed is True
+ assert response.http_request.headers.get("X-Stainless-Lang") == "python"
+ bridge = response.parse()
+ assert_matches_type(BridgeListResponse, bridge, path=["response"])
+
+ @parametrize
+ def test_streaming_response_list(self, client: BeeperDesktop) -> None:
+ with client.bridges.with_streaming_response.list() as response:
+ assert not response.is_closed
+ assert response.http_request.headers.get("X-Stainless-Lang") == "python"
+
+ bridge = response.parse()
+ assert_matches_type(BridgeListResponse, bridge, path=["response"])
+
+ assert cast(Any, response.is_closed) is True
+
+ @parametrize
+ def test_method_retrieve_capabilities(self, client: BeeperDesktop) -> None:
+ bridge = client.bridges.retrieve_capabilities(
+ "local-whatsapp",
+ )
+ assert_matches_type(ProvisioningCapabilities, bridge, path=["response"])
+
+ @parametrize
+ def test_raw_response_retrieve_capabilities(self, client: BeeperDesktop) -> None:
+ response = client.bridges.with_raw_response.retrieve_capabilities(
+ "local-whatsapp",
+ )
+
+ assert response.is_closed is True
+ assert response.http_request.headers.get("X-Stainless-Lang") == "python"
+ bridge = response.parse()
+ assert_matches_type(ProvisioningCapabilities, bridge, path=["response"])
+
+ @parametrize
+ def test_streaming_response_retrieve_capabilities(self, client: BeeperDesktop) -> None:
+ with client.bridges.with_streaming_response.retrieve_capabilities(
+ "local-whatsapp",
+ ) as response:
+ assert not response.is_closed
+ assert response.http_request.headers.get("X-Stainless-Lang") == "python"
+
+ bridge = response.parse()
+ assert_matches_type(ProvisioningCapabilities, bridge, path=["response"])
+
+ assert cast(Any, response.is_closed) is True
+
+ @parametrize
+ def test_path_params_retrieve_capabilities(self, client: BeeperDesktop) -> None:
+ with pytest.raises(ValueError, match=r"Expected a non-empty value for `bridge_id` but received ''"):
+ client.bridges.with_raw_response.retrieve_capabilities(
+ "",
+ )
+
+
+class TestAsyncBridges:
+ parametrize = pytest.mark.parametrize(
+ "async_client", [False, True, {"http_client": "aiohttp"}], indirect=True, ids=["loose", "strict", "aiohttp"]
+ )
+
+ @parametrize
+ async def test_method_retrieve(self, async_client: AsyncBeeperDesktop) -> None:
+ bridge = await async_client.bridges.retrieve(
+ "local-whatsapp",
+ )
+ assert_matches_type(BridgeRetrieveResponse, bridge, path=["response"])
+
+ @parametrize
+ async def test_raw_response_retrieve(self, async_client: AsyncBeeperDesktop) -> None:
+ response = await async_client.bridges.with_raw_response.retrieve(
+ "local-whatsapp",
+ )
+
+ assert response.is_closed is True
+ assert response.http_request.headers.get("X-Stainless-Lang") == "python"
+ bridge = await response.parse()
+ assert_matches_type(BridgeRetrieveResponse, bridge, path=["response"])
+
+ @parametrize
+ async def test_streaming_response_retrieve(self, async_client: AsyncBeeperDesktop) -> None:
+ async with async_client.bridges.with_streaming_response.retrieve(
+ "local-whatsapp",
+ ) as response:
+ assert not response.is_closed
+ assert response.http_request.headers.get("X-Stainless-Lang") == "python"
+
+ bridge = await response.parse()
+ assert_matches_type(BridgeRetrieveResponse, bridge, path=["response"])
+
+ assert cast(Any, response.is_closed) is True
+
+ @parametrize
+ async def test_path_params_retrieve(self, async_client: AsyncBeeperDesktop) -> None:
+ with pytest.raises(ValueError, match=r"Expected a non-empty value for `bridge_id` but received ''"):
+ await async_client.bridges.with_raw_response.retrieve(
+ "",
+ )
+
+ @parametrize
+ async def test_method_list(self, async_client: AsyncBeeperDesktop) -> None:
+ bridge = await async_client.bridges.list()
+ assert_matches_type(BridgeListResponse, bridge, path=["response"])
+
+ @parametrize
+ async def test_raw_response_list(self, async_client: AsyncBeeperDesktop) -> None:
+ response = await async_client.bridges.with_raw_response.list()
+
+ assert response.is_closed is True
+ assert response.http_request.headers.get("X-Stainless-Lang") == "python"
+ bridge = await response.parse()
+ assert_matches_type(BridgeListResponse, bridge, path=["response"])
+
+ @parametrize
+ async def test_streaming_response_list(self, async_client: AsyncBeeperDesktop) -> None:
+ async with async_client.bridges.with_streaming_response.list() as response:
+ assert not response.is_closed
+ assert response.http_request.headers.get("X-Stainless-Lang") == "python"
+
+ bridge = await response.parse()
+ assert_matches_type(BridgeListResponse, bridge, path=["response"])
+
+ assert cast(Any, response.is_closed) is True
+
+ @parametrize
+ async def test_method_retrieve_capabilities(self, async_client: AsyncBeeperDesktop) -> None:
+ bridge = await async_client.bridges.retrieve_capabilities(
+ "local-whatsapp",
+ )
+ assert_matches_type(ProvisioningCapabilities, bridge, path=["response"])
+
+ @parametrize
+ async def test_raw_response_retrieve_capabilities(self, async_client: AsyncBeeperDesktop) -> None:
+ response = await async_client.bridges.with_raw_response.retrieve_capabilities(
+ "local-whatsapp",
+ )
+
+ assert response.is_closed is True
+ assert response.http_request.headers.get("X-Stainless-Lang") == "python"
+ bridge = await response.parse()
+ assert_matches_type(ProvisioningCapabilities, bridge, path=["response"])
+
+ @parametrize
+ async def test_streaming_response_retrieve_capabilities(self, async_client: AsyncBeeperDesktop) -> None:
+ async with async_client.bridges.with_streaming_response.retrieve_capabilities(
+ "local-whatsapp",
+ ) as response:
+ assert not response.is_closed
+ assert response.http_request.headers.get("X-Stainless-Lang") == "python"
+
+ bridge = await response.parse()
+ assert_matches_type(ProvisioningCapabilities, bridge, path=["response"])
+
+ assert cast(Any, response.is_closed) is True
+
+ @parametrize
+ async def test_path_params_retrieve_capabilities(self, async_client: AsyncBeeperDesktop) -> None:
+ with pytest.raises(ValueError, match=r"Expected a non-empty value for `bridge_id` but received ''"):
+ await async_client.bridges.with_raw_response.retrieve_capabilities(
+ "",
+ )
diff --git a/tests/test_models.py b/tests/test_models.py
index e71da85..d228ea1 100644
--- a/tests/test_models.py
+++ b/tests/test_models.py
@@ -1,7 +1,8 @@
import json
-from typing import TYPE_CHECKING, Any, Dict, List, Union, Optional, cast
+from typing import TYPE_CHECKING, Any, Dict, List, Union, Iterable, Optional, cast
from datetime import datetime, timezone
-from typing_extensions import Literal, Annotated, TypeAliasType
+from collections import deque
+from typing_extensions import Literal, Annotated, TypedDict, TypeAliasType
import pytest
import pydantic
@@ -9,7 +10,7 @@
from beeper_desktop_api._utils import PropertyInfo
from beeper_desktop_api._compat import PYDANTIC_V1, parse_obj, model_dump, model_json
-from beeper_desktop_api._models import DISCRIMINATOR_CACHE, BaseModel, construct_type
+from beeper_desktop_api._models import DISCRIMINATOR_CACHE, BaseModel, EagerIterable, construct_type
class BasicModel(BaseModel):
@@ -961,3 +962,56 @@ def __getattr__(self, attr: str) -> Item: ...
assert model.a.prop == 1
assert isinstance(model.a, Item)
assert model.other == "foo"
+
+
+# NOTE: Workaround for Pydantic Iterable behavior.
+# Iterable fields are replaced with a ValidatorIterator and may be consumed
+# during serialization, which can cause subsequent dumps to return empty data.
+# See: https://github.com/pydantic/pydantic/issues/9541
+@pytest.mark.parametrize(
+ "data, expected_validated",
+ [
+ ([1, 2, 3], [1, 2, 3]),
+ ((1, 2, 3), (1, 2, 3)),
+ (set([1, 2, 3]), set([1, 2, 3])),
+ (iter([1, 2, 3]), [1, 2, 3]),
+ ([], []),
+ ((x for x in [1, 2, 3]), [1, 2, 3]),
+ (map(lambda x: x, [1, 2, 3]), [1, 2, 3]),
+ (frozenset([1, 2, 3]), frozenset([1, 2, 3])),
+ (deque([1, 2, 3]), deque([1, 2, 3])),
+ ],
+ ids=["list", "tuple", "set", "iterator", "empty", "generator", "map", "frozenset", "deque"],
+)
+@pytest.mark.skipif(PYDANTIC_V1, reason="this is only supported in pydantic v2")
+def test_iterable_construction(data: Iterable[int], expected_validated: Iterable[int]) -> None:
+ class TypeWithIterable(TypedDict):
+ items: EagerIterable[int]
+
+ class Model(BaseModel):
+ data: TypeWithIterable
+
+ m = Model.model_validate({"data": {"items": data}})
+ assert m.data["items"] == expected_validated
+
+ # Verify repeated dumps don't lose data (the original bug)
+ assert m.model_dump()["data"]["items"] == list(expected_validated)
+ assert m.model_dump()["data"]["items"] == list(expected_validated)
+
+
+@pytest.mark.skipif(PYDANTIC_V1, reason="this is only supported in pydantic v2")
+def test_iterable_construction_str_falls_back_to_list() -> None:
+ # str is iterable (over chars), but str(list_of_chars) produces the list's repr
+ # rather than reconstructing a string from items. We special-case str to fall
+ # back to list instead of attempting reconstruction.
+ class TypeWithIterable(TypedDict):
+ items: EagerIterable[str]
+
+ class Model(BaseModel):
+ data: TypeWithIterable
+
+ m = Model.model_validate({"data": {"items": "hello"}})
+
+ # falls back to list of chars rather than calling str(["h", "e", "l", "l", "o"])
+ assert m.data["items"] == ["h", "e", "l", "l", "o"]
+ assert m.model_dump()["data"]["items"] == ["h", "e", "l", "l", "o"]