planvortex 0.5.0__tar.gz → 0.6.0__tar.gz

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (109) hide show
  1. {planvortex-0.5.0 → planvortex-0.6.0}/CHANGELOG.md +40 -0
  2. {planvortex-0.5.0 → planvortex-0.6.0}/PKG-INFO +4 -4
  3. {planvortex-0.5.0 → planvortex-0.6.0}/README.md +3 -3
  4. {planvortex-0.5.0 → planvortex-0.6.0}/openapi/planvortex.openapi.json +24 -14
  5. {planvortex-0.5.0 → planvortex-0.6.0}/pyproject.toml +1 -1
  6. {planvortex-0.5.0 → planvortex-0.6.0}/src/planvortex/_core/errors.py +15 -6
  7. {planvortex-0.5.0 → planvortex-0.6.0}/src/planvortex/_generated/models.py +20 -9
  8. {planvortex-0.5.0 → planvortex-0.6.0}/src/planvortex/_shapes.py +2 -1
  9. {planvortex-0.5.0 → planvortex-0.6.0}/src/planvortex/_version.py +1 -1
  10. {planvortex-0.5.0 → planvortex-0.6.0}/src/planvortex/types.py +13 -0
  11. {planvortex-0.5.0 → planvortex-0.6.0}/tests/test_core.py +12 -0
  12. {planvortex-0.5.0 → planvortex-0.6.0}/tests/test_example_connect.py +1 -1
  13. {planvortex-0.5.0 → planvortex-0.6.0}/tests/test_example_publish.py +1 -1
  14. {planvortex-0.5.0 → planvortex-0.6.0}/tests/test_organizations.py +2 -2
  15. {planvortex-0.5.0 → planvortex-0.6.0}/.gitignore +0 -0
  16. {planvortex-0.5.0 → planvortex-0.6.0}/LICENSE +0 -0
  17. {planvortex-0.5.0 → planvortex-0.6.0}/scripts/build_docs.py +0 -0
  18. {planvortex-0.5.0 → planvortex-0.6.0}/scripts/check_packaging.py +0 -0
  19. {planvortex-0.5.0 → planvortex-0.6.0}/scripts/generate_models.py +0 -0
  20. {planvortex-0.5.0 → planvortex-0.6.0}/scripts/generate_sync.py +0 -0
  21. {planvortex-0.5.0 → planvortex-0.6.0}/scripts/route_coverage.py +0 -0
  22. {planvortex-0.5.0 → planvortex-0.6.0}/src/planvortex/__init__.py +0 -0
  23. {planvortex-0.5.0 → planvortex-0.6.0}/src/planvortex/_client.py +0 -0
  24. {planvortex-0.5.0 → planvortex-0.6.0}/src/planvortex/_client_sync.py +0 -0
  25. {planvortex-0.5.0 → planvortex-0.6.0}/src/planvortex/_core/__init__.py +0 -0
  26. {planvortex-0.5.0 → planvortex-0.6.0}/src/planvortex/_core/auth.py +0 -0
  27. {planvortex-0.5.0 → planvortex-0.6.0}/src/planvortex/_core/auth_sync.py +0 -0
  28. {planvortex-0.5.0 → planvortex-0.6.0}/src/planvortex/_core/files.py +0 -0
  29. {planvortex-0.5.0 → planvortex-0.6.0}/src/planvortex/_core/http.py +0 -0
  30. {planvortex-0.5.0 → planvortex-0.6.0}/src/planvortex/_core/http_sync.py +0 -0
  31. {planvortex-0.5.0 → planvortex-0.6.0}/src/planvortex/_core/pagination.py +0 -0
  32. {planvortex-0.5.0 → planvortex-0.6.0}/src/planvortex/_core/query.py +0 -0
  33. {planvortex-0.5.0 → planvortex-0.6.0}/src/planvortex/_core/transport.py +0 -0
  34. {planvortex-0.5.0 → planvortex-0.6.0}/src/planvortex/_generated/__init__.py +0 -0
  35. {planvortex-0.5.0 → planvortex-0.6.0}/src/planvortex/py.typed +0 -0
  36. {planvortex-0.5.0 → planvortex-0.6.0}/src/planvortex/resources/__init__.py +0 -0
  37. {planvortex-0.5.0 → planvortex-0.6.0}/src/planvortex/resources/accounts.py +0 -0
  38. {planvortex-0.5.0 → planvortex-0.6.0}/src/planvortex/resources/ai_plans.py +0 -0
  39. {planvortex-0.5.0 → planvortex-0.6.0}/src/planvortex/resources/apps.py +0 -0
  40. {planvortex-0.5.0 → planvortex-0.6.0}/src/planvortex/resources/base.py +0 -0
  41. {planvortex-0.5.0 → planvortex-0.6.0}/src/planvortex/resources/catalog.py +0 -0
  42. {planvortex-0.5.0 → planvortex-0.6.0}/src/planvortex/resources/clients.py +0 -0
  43. {planvortex-0.5.0 → planvortex-0.6.0}/src/planvortex/resources/comments.py +0 -0
  44. {planvortex-0.5.0 → planvortex-0.6.0}/src/planvortex/resources/contacts.py +0 -0
  45. {planvortex-0.5.0 → planvortex-0.6.0}/src/planvortex/resources/dashboard.py +0 -0
  46. {planvortex-0.5.0 → planvortex-0.6.0}/src/planvortex/resources/integrations.py +0 -0
  47. {planvortex-0.5.0 → planvortex-0.6.0}/src/planvortex/resources/messages.py +0 -0
  48. {planvortex-0.5.0 → planvortex-0.6.0}/src/planvortex/resources/organizations.py +0 -0
  49. {planvortex-0.5.0 → planvortex-0.6.0}/src/planvortex/resources/products.py +0 -0
  50. {planvortex-0.5.0 → planvortex-0.6.0}/src/planvortex/resources/publications.py +0 -0
  51. {planvortex-0.5.0 → planvortex-0.6.0}/src/planvortex/resources/uploads.py +0 -0
  52. {planvortex-0.5.0 → planvortex-0.6.0}/src/planvortex/resources_sync/__init__.py +0 -0
  53. {planvortex-0.5.0 → planvortex-0.6.0}/src/planvortex/resources_sync/accounts.py +0 -0
  54. {planvortex-0.5.0 → planvortex-0.6.0}/src/planvortex/resources_sync/ai_plans.py +0 -0
  55. {planvortex-0.5.0 → planvortex-0.6.0}/src/planvortex/resources_sync/apps.py +0 -0
  56. {planvortex-0.5.0 → planvortex-0.6.0}/src/planvortex/resources_sync/base.py +0 -0
  57. {planvortex-0.5.0 → planvortex-0.6.0}/src/planvortex/resources_sync/catalog.py +0 -0
  58. {planvortex-0.5.0 → planvortex-0.6.0}/src/planvortex/resources_sync/clients.py +0 -0
  59. {planvortex-0.5.0 → planvortex-0.6.0}/src/planvortex/resources_sync/comments.py +0 -0
  60. {planvortex-0.5.0 → planvortex-0.6.0}/src/planvortex/resources_sync/contacts.py +0 -0
  61. {planvortex-0.5.0 → planvortex-0.6.0}/src/planvortex/resources_sync/dashboard.py +0 -0
  62. {planvortex-0.5.0 → planvortex-0.6.0}/src/planvortex/resources_sync/integrations.py +0 -0
  63. {planvortex-0.5.0 → planvortex-0.6.0}/src/planvortex/resources_sync/messages.py +0 -0
  64. {planvortex-0.5.0 → planvortex-0.6.0}/src/planvortex/resources_sync/organizations.py +0 -0
  65. {planvortex-0.5.0 → planvortex-0.6.0}/src/planvortex/resources_sync/products.py +0 -0
  66. {planvortex-0.5.0 → planvortex-0.6.0}/src/planvortex/resources_sync/publications.py +0 -0
  67. {planvortex-0.5.0 → planvortex-0.6.0}/src/planvortex/resources_sync/uploads.py +0 -0
  68. {planvortex-0.5.0 → planvortex-0.6.0}/src/planvortex/webhooks.py +0 -0
  69. {planvortex-0.5.0 → planvortex-0.6.0}/tests/__init__.py +0 -0
  70. {planvortex-0.5.0 → planvortex-0.6.0}/tests/conftest.py +0 -0
  71. {planvortex-0.5.0 → planvortex-0.6.0}/tests/contrato.py +0 -0
  72. {planvortex-0.5.0 → planvortex-0.6.0}/tests/live/__init__.py +0 -0
  73. {planvortex-0.5.0 → planvortex-0.6.0}/tests/live/conftest.py +0 -0
  74. {planvortex-0.5.0 → planvortex-0.6.0}/tests/live/test_auth.py +0 -0
  75. {planvortex-0.5.0 → planvortex-0.6.0}/tests/live/test_catalog.py +0 -0
  76. {planvortex-0.5.0 → planvortex-0.6.0}/tests/live/test_connect_flow.py +0 -0
  77. {planvortex-0.5.0 → planvortex-0.6.0}/tests/live/test_errors.py +0 -0
  78. {planvortex-0.5.0 → planvortex-0.6.0}/tests/live/test_publish.py +0 -0
  79. {planvortex-0.5.0 → planvortex-0.6.0}/tests/live/test_read.py +0 -0
  80. {planvortex-0.5.0 → planvortex-0.6.0}/tests/test_accounts.py +0 -0
  81. {planvortex-0.5.0 → planvortex-0.6.0}/tests/test_ai_plans.py +0 -0
  82. {planvortex-0.5.0 → planvortex-0.6.0}/tests/test_apps.py +0 -0
  83. {planvortex-0.5.0 → planvortex-0.6.0}/tests/test_catalog.py +0 -0
  84. {planvortex-0.5.0 → planvortex-0.6.0}/tests/test_client.py +0 -0
  85. {planvortex-0.5.0 → planvortex-0.6.0}/tests/test_clients.py +0 -0
  86. {planvortex-0.5.0 → planvortex-0.6.0}/tests/test_comments.py +0 -0
  87. {planvortex-0.5.0 → planvortex-0.6.0}/tests/test_contacts.py +0 -0
  88. {planvortex-0.5.0 → planvortex-0.6.0}/tests/test_dashboard.py +0 -0
  89. {planvortex-0.5.0 → planvortex-0.6.0}/tests/test_example_comments.py +0 -0
  90. {planvortex-0.5.0 → planvortex-0.6.0}/tests/test_example_schedule.py +0 -0
  91. {planvortex-0.5.0 → planvortex-0.6.0}/tests/test_example_webhooks.py +0 -0
  92. {planvortex-0.5.0 → planvortex-0.6.0}/tests/test_files.py +0 -0
  93. {planvortex-0.5.0 → planvortex-0.6.0}/tests/test_generate_models.py +0 -0
  94. {planvortex-0.5.0 → planvortex-0.6.0}/tests/test_generate_sync.py +0 -0
  95. {planvortex-0.5.0 → planvortex-0.6.0}/tests/test_integrations.py +0 -0
  96. {planvortex-0.5.0 → planvortex-0.6.0}/tests/test_messages.py +0 -0
  97. {planvortex-0.5.0 → planvortex-0.6.0}/tests/test_models.py +0 -0
  98. {planvortex-0.5.0 → planvortex-0.6.0}/tests/test_openapi_freshness.py +0 -0
  99. {planvortex-0.5.0 → planvortex-0.6.0}/tests/test_packaging.py +0 -0
  100. {planvortex-0.5.0 → planvortex-0.6.0}/tests/test_pagination.py +0 -0
  101. {planvortex-0.5.0 → planvortex-0.6.0}/tests/test_products.py +0 -0
  102. {planvortex-0.5.0 → planvortex-0.6.0}/tests/test_publications.py +0 -0
  103. {planvortex-0.5.0 → planvortex-0.6.0}/tests/test_query_parity.py +0 -0
  104. {planvortex-0.5.0 → planvortex-0.6.0}/tests/test_readme.py +0 -0
  105. {planvortex-0.5.0 → planvortex-0.6.0}/tests/test_route_coverage.py +0 -0
  106. {planvortex-0.5.0 → planvortex-0.6.0}/tests/test_shapes_parity.py +0 -0
  107. {planvortex-0.5.0 → planvortex-0.6.0}/tests/test_types.py +0 -0
  108. {planvortex-0.5.0 → planvortex-0.6.0}/tests/test_uploads.py +0 -0
  109. {planvortex-0.5.0 → planvortex-0.6.0}/tests/test_webhooks.py +0 -0
@@ -4,6 +4,46 @@ All notable changes to this package are documented here. The format follows
4
4
  [Keep a Changelog](https://keepachangelog.com/en/1.1.0/), and this project adheres to
5
5
  [Semantic Versioning](https://semver.org/spec/v2.0.0.html).
6
6
 
7
+ ## [0.6.0] - 2026-09-03
8
+
9
+ **Publications are unlimited on every plan**, so the quota that said otherwise is gone from the
10
+ types.
11
+
12
+ It was never a number this package invented: `PlanData["publications"]` came from the spec, and the
13
+ server stopped charging for it on 02-09-2026. What stayed behind was worse than a wrong number — it
14
+ was a key mypy kept promising, so `limits["publications"]` type-checked and raised `KeyError` at
15
+ runtime.
16
+
17
+ ### Changed
18
+
19
+ - **BREAKING (types only): `PlanData` no longer has a `publications` key.** The count moved to the
20
+ new `PlanUseData`, which is what `actual_use` has always been in practice: the same shape as a
21
+ plan plus `publications`, and `NotRequired` because a brand-new organization has spent nothing.
22
+ Nothing changes on the wire.
23
+ - `OrganizationUse["actual_use"]` is a `PlanUseData`; `actual_asigned` stays a `PlanData`, which is
24
+ the real asymmetry: what a child organization was handed cannot include a metric.
25
+ - The `publish` example prints publications as what they are — a monthly count with no ceiling —
26
+ instead of `12 de 100`. What throttles publishing is rate (per hour and account, and the
27
+ per-network daily caps in `GET /social_limits`), not the plan.
28
+ - **The `publication` error family now reaches 979, not 960.** Everything above the old ceiling was
29
+ falling outside every range and arriving as a bare `PlanVortexError`: the Bluesky, Discord,
30
+ Telegram and Threads codes (961-977) and, more to the point, the two rate brakes that replaced the
31
+ monthly quota — **978** (publishing too fast on this account) and **979** (that network's daily
32
+ cap). `except PublicationError` now catches them.
33
+
34
+ ### Added
35
+
36
+ **The public API is on every plan now, the free one included** — it used to be a Custom-plan
37
+ feature — and the types follow the three things that came with opening it.
38
+
39
+ - `PlanData["apps"]`: how many apps the plan allows (1 on Free, 2 on Basic, 5 on Pro, 10 on Custom).
40
+ Each app is a `client_id` with a secret, so each one is a key to the whole API. Unlike accounts,
41
+ users or storage it is **not** split between organizations — an app belongs to the client.
42
+ - Three error codes, and the two ranges that were hiding them: **545** (the plan's API rate limit,
43
+ which arrives as a `429` with `Retry-After`) and **546** (the account's email must be verified to
44
+ create an app) are `AuthError`, so that range now runs to 546; **1308** (no more apps fit in this
45
+ plan) is a `PlanLimitError`, so that one runs to 1308.
46
+
7
47
  ## [0.5.0] - 2026-09-02
8
48
 
9
49
  **Threads is the twelfth network**, and the types now know it.
@@ -1,6 +1,6 @@
1
1
  Metadata-Version: 2.5
2
2
  Name: planvortex
3
- Version: 0.5.0
3
+ Version: 0.6.0
4
4
  Summary: Official Python client for the PlanVortex API
5
5
  Project-URL: Homepage, https://planvortex.com/developers
6
6
  Project-URL: Documentation, https://taliasoftworks.github.io/PlanVortexPython/
@@ -242,15 +242,15 @@ memorising numbers:
242
242
 
243
243
  | Codes | Family | Exception |
244
244
  |---|---|---|
245
- | 500-544 | `auth` | `AuthError` |
245
+ | 500-546 | `auth` | `AuthError` |
246
246
  | 601-612 | `user` | `UserError` |
247
247
  | 700-715 | `account` | `AccountError` |
248
248
  | 800-810 | `file` | `FileError` |
249
- | 900-960 | `publication` | `PublicationError` |
249
+ | 900-979 | `publication` | `PublicationError` |
250
250
  | 1000-1003 | `general` | `PlanVortexError` |
251
251
  | 1100-1111 | `organization` | `OrganizationError` |
252
252
  | 1200-1207 | `role` | `PlanVortexError` |
253
- | 1300-1307, 1400-1408 | `plan_limit` | `PlanLimitError` |
253
+ | 1300-1308, 1400-1408 | `plan_limit` | `PlanLimitError` |
254
254
  | 1500-1512 | `messaging` | `MessagingError` |
255
255
  | 1600-1601 | `contact` | `ContactError` |
256
256
  | 1900-1906 | `payment` | `PlanVortexError` |
@@ -212,15 +212,15 @@ memorising numbers:
212
212
 
213
213
  | Codes | Family | Exception |
214
214
  |---|---|---|
215
- | 500-544 | `auth` | `AuthError` |
215
+ | 500-546 | `auth` | `AuthError` |
216
216
  | 601-612 | `user` | `UserError` |
217
217
  | 700-715 | `account` | `AccountError` |
218
218
  | 800-810 | `file` | `FileError` |
219
- | 900-960 | `publication` | `PublicationError` |
219
+ | 900-979 | `publication` | `PublicationError` |
220
220
  | 1000-1003 | `general` | `PlanVortexError` |
221
221
  | 1100-1111 | `organization` | `OrganizationError` |
222
222
  | 1200-1207 | `role` | `PlanVortexError` |
223
- | 1300-1307, 1400-1408 | `plan_limit` | `PlanLimitError` |
223
+ | 1300-1308, 1400-1408 | `plan_limit` | `PlanLimitError` |
224
224
  | 1500-1512 | `messaging` | `MessagingError` |
225
225
  | 1600-1601 | `contact` | `ContactError` |
226
226
  | 1900-1906 | `payment` | `PlanVortexError` |
@@ -2449,7 +2449,6 @@
2449
2449
  "name": "Client X",
2450
2450
  "actual_plan": {
2451
2451
  "accounts": 4,
2452
- "publications": 150,
2453
2452
  "users": 1,
2454
2453
  "space": 0.25,
2455
2454
  "integrations": 1,
@@ -2625,7 +2624,6 @@
2625
2624
  "value": {
2626
2625
  "actual_plan": {
2627
2626
  "accounts": 8,
2628
- "publications": 300,
2629
2627
  "users": 3,
2630
2628
  "space": 1,
2631
2629
  "integrations": 3,
@@ -6024,7 +6022,6 @@
6024
6022
  "value": {
6025
6023
  "actual_plan": {
6026
6024
  "accounts": 8,
6027
- "publications": 300,
6028
6025
  "users": 3,
6029
6026
  "space": 1,
6030
6027
  "integrations": 3,
@@ -6343,7 +6340,6 @@
6343
6340
  "name": "Client X - Spain",
6344
6341
  "actual_plan": {
6345
6342
  "accounts": 3,
6346
- "publications": 100,
6347
6343
  "users": 1,
6348
6344
  "space": 0.25,
6349
6345
  "integrations": 0,
@@ -10702,7 +10698,7 @@
10702
10698
  "actual_use": {
10703
10699
  "allOf": [
10704
10700
  {
10705
- "$ref": "#/components/schemas/PlanData"
10701
+ "$ref": "#/components/schemas/PlanUseData"
10706
10702
  }
10707
10703
  ],
10708
10704
  "description": "Current consumption across every organization of the client. **Only present with `getUse=true`.** `twitter_credits` and `ai_credits` are what has been spent this calendar month."
@@ -12079,7 +12075,7 @@
12079
12075
  "actual_use": {
12080
12076
  "allOf": [
12081
12077
  {
12082
- "$ref": "#/components/schemas/PlanData"
12078
+ "$ref": "#/components/schemas/PlanUseData"
12083
12079
  }
12084
12080
  ],
12085
12081
  "description": "What the organization and its children are consuming right now."
@@ -12265,7 +12261,7 @@
12265
12261
  },
12266
12262
  "code": {
12267
12263
  "type": "integer",
12268
- "description": "PlanVortex error code. Ranges: 500-544 auth, tokens and client apps · 601-612 user · 700-715 social accounts · 800-810 files · 900-960 publications · 1000-1003 general · 1100-1111 organizations · 1200-1207 roles · 1300-1307 client plan · 1400-1408 organization plan · 1500-1512 messaging · 1600-1601 contacts · 1900-1906 payments · 2000-2099 products · 2100-2199 AI plans · 2200-2299 integrations."
12264
+ "description": "PlanVortex error code. Ranges: 500-546 auth, tokens and client apps · 601-612 user · 700-715 social accounts · 800-810 files · 900-979 publications · 1000-1003 general · 1100-1111 organizations · 1200-1207 roles · 1300-1308 client plan · 1400-1408 organization plan · 1500-1512 messaging · 1600-1601 contacts · 1900-1906 payments · 2000-2099 products · 2100-2199 AI plans · 2200-2299 integrations."
12269
12265
  },
12270
12266
  "data": {
12271
12267
  "additionalProperties": true,
@@ -12943,7 +12939,7 @@
12943
12939
  "actual_use": {
12944
12940
  "allOf": [
12945
12941
  {
12946
- "$ref": "#/components/schemas/PlanData"
12942
+ "$ref": "#/components/schemas/PlanUseData"
12947
12943
  }
12948
12944
  ],
12949
12945
  "description": "Current consumption. **Only present with `getUse=true`.** `twitter_credits` and `ai_credits` are what has been spent in the current calendar month; the rest is what exists right now."
@@ -13385,7 +13381,6 @@
13385
13381
  "description": "The resources a plan grants. On a client it is what was contracted; on an organization, the slice of it that was assigned. The sum across all the organizations of a client can never exceed what the client has contracted.",
13386
13382
  "required": [
13387
13383
  "accounts",
13388
- "publications",
13389
13384
  "users",
13390
13385
  "space",
13391
13386
  "integrations"
@@ -13395,10 +13390,6 @@
13395
13390
  "type": "integer",
13396
13391
  "description": "Social accounts that may be connected."
13397
13392
  },
13398
- "publications": {
13399
- "type": "integer",
13400
- "description": "Publications that may be sent per month."
13401
- },
13402
13393
  "users": {
13403
13394
  "type": "integer",
13404
13395
  "description": "Users with access."
@@ -13409,7 +13400,11 @@
13409
13400
  },
13410
13401
  "integrations": {
13411
13402
  "type": "integer",
13412
- "description": "Connections to a third-party tool material is pulled from (Google Drive, an RSS feed). Not the same thing as an app: an app is API access, and it is a Custom-plan feature of its own."
13403
+ "description": "Connections to a third-party tool material is pulled from (Google Drive, an RSS feed). Not the same thing as an app: an app is API access, and it has its own allowance in `apps`."
13404
+ },
13405
+ "apps": {
13406
+ "type": "integer",
13407
+ "description": "Apps this plan allows: 1 on Free, 2 on Basic, 5 on Pro, 10 on Custom. Each one is a `client_id` with a secret, so each one is a key to the whole public API — which every plan has, Free included. Unlike accounts, users or storage this is NOT split between organizations: an app belongs to the client. On a use payload it is how many exist right now."
13413
13408
  },
13414
13409
  "twitter_credits": {
13415
13410
  "type": "integer",
@@ -13433,6 +13428,21 @@
13433
13428
  }
13434
13429
  }
13435
13430
  },
13431
+ "PlanUseData": {
13432
+ "type": "object",
13433
+ "description": "What is being consumed right now. Same shape as PlanData plus `publications`, which is counted but neither charged nor split between organizations.",
13434
+ "allOf": [
13435
+ {
13436
+ "$ref": "#/components/schemas/PlanData"
13437
+ }
13438
+ ],
13439
+ "properties": {
13440
+ "publications": {
13441
+ "type": "integer",
13442
+ "description": "Publications created in the current calendar month. A METRIC, not a quota: publications are unlimited on every plan, so there is no limit to compare it against. What throttles publishing is rate, not plan — see the per-hour and per-network daily caps."
13443
+ }
13444
+ }
13445
+ },
13436
13446
  "ProductsProduct": {
13437
13447
  "allOf": [
13438
13448
  {
@@ -9,7 +9,7 @@ name = "planvortex"
9
9
  # entonces `project.version` desapareceria del pyproject y el paso del workflow de release que
10
10
  # compara el tag con la version —que lo lee con `tomllib`— dejaria de encontrarla. Se quedan las dos
11
11
  # y las ata `tests/test_packaging.py`, que falla si se separan.
12
- version = "0.5.0"
12
+ version = "0.6.0"
13
13
  description = "Official Python client for the PlanVortex API"
14
14
  readme = "README.md"
15
15
  requires-python = ">=3.10"
@@ -27,15 +27,22 @@ class ErrorRange(NamedTuple):
27
27
  # Los mismos 16 rangos que publica `swagger/common.json`, `PLANVORTEX_ERROR_RANGES` del paquete de
28
28
  # Node y el apendice A del roadmap. SI SE TOCA UN RANGO, SE TOCAN LOS CINCO SITIOS.
29
29
  PLANVORTEX_ERROR_RANGES: tuple[ErrorRange, ...] = (
30
- ErrorRange(500, 544, "auth"),
30
+ # Hasta 546, no 544: la API publica se abrio a todos los planes y trajo dos codigos nuevos
31
+ # —545 (ritmo por plan, que sale 429) y 546 (correo sin verificar al crear una app)—.
32
+ ErrorRange(500, 546, "auth"),
31
33
  ErrorRange(601, 612, "user"),
32
34
  ErrorRange(700, 715, "account"),
33
35
  ErrorRange(800, 810, "file"),
34
- ErrorRange(900, 960, "publication"),
36
+ # El techo sube con el catalogo del servidor, y subir tarde no da un error: da un consejo
37
+ # equivocado. Los codigos de Bluesky, Discord, Telegram y Threads (961-977) y los dos frenos
38
+ # de ritmo de la fase de publicaciones ilimitadas (978, 979) nacieron por encima de 960 y
39
+ # caian fuera de toda familia.
40
+ ErrorRange(900, 979, "publication"),
35
41
  ErrorRange(1000, 1003, "general"),
36
42
  ErrorRange(1100, 1111, "organization"),
37
43
  ErrorRange(1200, 1207, "role"),
38
- ErrorRange(1300, 1307, "plan_limit"),
44
+ # 1308 es el tope de APPS del plan, que nacio al quitar el candado de precio de la API.
45
+ ErrorRange(1300, 1308, "plan_limit"),
39
46
  ErrorRange(1400, 1408, "plan_limit"),
40
47
  ErrorRange(1500, 1512, "messaging"),
41
48
  ErrorRange(1600, 1601, "contact"),
@@ -101,7 +108,7 @@ class PlanVortexError(Exception):
101
108
 
102
109
 
103
110
  class AuthError(PlanVortexError):
104
- """500-544 — tokens, client apps, permissions. Includes 501 and 522, the expired-token ones."""
111
+ """500-546 — tokens, client apps, permissions. Includes 501 and 522, the expired-token ones."""
105
112
 
106
113
 
107
114
  class UserError(PlanVortexError):
@@ -117,7 +124,9 @@ class FileError(PlanVortexError):
117
124
 
118
125
 
119
126
  class PublicationError(PlanVortexError):
120
- """900-960 — publications, including per-network limits (characters, images, duration)."""
127
+ """900-979 — publications, including per-network limits (characters, images, duration) and the
128
+ two rate brakes: 978 (publishing too fast on this account) and 979 (that network's daily cap).
129
+ """
121
130
 
122
131
 
123
132
  class OrganizationError(PlanVortexError):
@@ -125,7 +134,7 @@ class OrganizationError(PlanVortexError):
125
134
 
126
135
 
127
136
  class PlanLimitError(PlanVortexError):
128
- """1300-1307 and 1400-1408 — the plan's quota, on the client or on the organization.
137
+ """1300-1308 and 1400-1408 — the plan's quota, on the client or on the organization.
129
138
 
130
139
  This is the error an integrator actually wants to tell apart: it is not fixed by retrying, it is
131
140
  fixed by changing plan. That is why both ranges share a class.
@@ -1072,7 +1072,7 @@ class Error1(TypedDict):
1072
1072
  """
1073
1073
  code: int
1074
1074
  """
1075
- PlanVortex error code. Ranges: 500-544 auth, tokens and client apps · 601-612 user · 700-715 social accounts · 800-810 files · 900-960 publications · 1000-1003 general · 1100-1111 organizations · 1200-1207 roles · 1300-1307 client plan · 1400-1408 organization plan · 1500-1512 messaging · 1600-1601 contacts · 1900-1906 payments · 2000-2099 products · 2100-2199 AI plans · 2200-2299 integrations.
1075
+ PlanVortex error code. Ranges: 500-546 auth, tokens and client apps · 601-612 user · 700-715 social accounts · 800-810 files · 900-979 publications · 1000-1003 general · 1100-1111 organizations · 1200-1207 roles · 1300-1308 client plan · 1400-1408 organization plan · 1500-1512 messaging · 1600-1601 contacts · 1900-1906 payments · 2000-2099 products · 2100-2199 AI plans · 2200-2299 integrations.
1076
1076
  """
1077
1077
  data: NotRequired[dict[str, Any]]
1078
1078
  """
@@ -1553,10 +1553,6 @@ class PlanData(TypedDict):
1553
1553
  """
1554
1554
  Social accounts that may be connected.
1555
1555
  """
1556
- publications: int
1557
- """
1558
- Publications that may be sent per month.
1559
- """
1560
1556
  users: int
1561
1557
  """
1562
1558
  Users with access.
@@ -1567,7 +1563,11 @@ class PlanData(TypedDict):
1567
1563
  """
1568
1564
  integrations: int
1569
1565
  """
1570
- Connections to a third-party tool material is pulled from (Google Drive, an RSS feed). Not the same thing as an app: an app is API access, and it is a Custom-plan feature of its own.
1566
+ Connections to a third-party tool material is pulled from (Google Drive, an RSS feed). Not the same thing as an app: an app is API access, and it has its own allowance in `apps`.
1567
+ """
1568
+ apps: NotRequired[int]
1569
+ """
1570
+ Apps this plan allows: 1 on Free, 2 on Basic, 5 on Pro, 10 on Custom. Each one is a `client_id` with a secret, so each one is a key to the whole public API — which every plan has, Free included. Unlike accounts, users or storage this is NOT split between organizations: an app belongs to the client. On a use payload it is how many exist right now.
1571
1571
  """
1572
1572
  twitter_credits: NotRequired[int]
1573
1573
  """
@@ -1591,6 +1591,17 @@ class PlanData(TypedDict):
1591
1591
  """
1592
1592
 
1593
1593
 
1594
+ class PlanUseData(PlanData):
1595
+ """
1596
+ What is being consumed right now. Same shape as PlanData plus `publications`, which is counted but neither charged nor split between organizations.
1597
+ """
1598
+
1599
+ publications: NotRequired[int]
1600
+ """
1601
+ Publications created in the current calendar month. A METRIC, not a quota: publications are unlimited on every plan, so there is no limit to compare it against. What throttles publishing is rate, not plan — see the per-hour and per-network daily caps.
1602
+ """
1603
+
1604
+
1594
1605
  class ProductsProductCatalog(TypedDict):
1595
1606
  """
1596
1607
  A Meta commerce catalogue, as the network returns it.
@@ -2399,7 +2410,7 @@ class DashboardDashboardPublicationRef(TypedDict):
2399
2410
 
2400
2411
 
2401
2412
  class DashboardPlanUse(TypedDict):
2402
- actual_use: PlanData
2413
+ actual_use: PlanUseData
2403
2414
  """
2404
2415
  What the organization and its children are consuming right now.
2405
2416
  """
@@ -2641,7 +2652,7 @@ class Organization(TypedDict):
2641
2652
  """
2642
2653
  The slice of the client's plan assigned to this organization. **Absent when nothing was assigned**, and then the organization shares whatever its nearest parent with a plan has — or, failing that, the client's unassigned remainder. Ask `GET /organizations/{id_organization}/limits` for the effective numbers instead of reading this.
2643
2654
  """
2644
- actual_use: NotRequired[PlanData]
2655
+ actual_use: NotRequired[PlanUseData]
2645
2656
  """
2646
2657
  Current consumption. **Only present with `getUse=true`.** `twitter_credits` and `ai_credits` are what has been spent in the current calendar month; the rest is what exists right now.
2647
2658
  """
@@ -2957,7 +2968,7 @@ class ClientsClient(TypedDict):
2957
2968
  """
2958
2969
  Read-only view of the client's own AI provider configuration (BYOK). Written via `PUT /clients/{id_client}/ai-settings`. API keys are stored encrypted and are NEVER returned: each scope only exposes provider, model and has_api_key.
2959
2970
  """
2960
- actual_use: NotRequired[PlanData]
2971
+ actual_use: NotRequired[PlanUseData]
2961
2972
  """
2962
2973
  Current consumption across every organization of the client. **Only present with `getUse=true`.** `twitter_credits` and `ai_credits` are what has been spent this calendar month.
2963
2974
  """
@@ -31,6 +31,7 @@ from planvortex._generated.models import (
31
31
  DashboardPublicationsSummary,
32
32
  Organization,
33
33
  PlanData,
34
+ PlanUseData,
34
35
  Publication,
35
36
  Upload,
36
37
  )
@@ -118,7 +119,7 @@ class OrganizationUse(TypedDict):
118
119
  with ``get_use=True``.
119
120
  """
120
121
 
121
- actual_use: NotRequired[PlanData]
122
+ actual_use: NotRequired[PlanUseData]
122
123
  actual_asigned: NotRequired[PlanData]
123
124
 
124
125
 
@@ -12,7 +12,7 @@ from __future__ import annotations
12
12
 
13
13
  import platform
14
14
 
15
- VERSION = "0.5.0"
15
+ VERSION = "0.6.0"
16
16
 
17
17
  # Sin barra final, y con el `/v1.0.0` dentro: la version de la API es parte de la ruta, no una
18
18
  # cabecera, asi que una `base_url` sin ella da 404 en todo.
@@ -289,6 +289,18 @@ CAREFUL WITH THE SUM: what is assigned to a client's organizations can never exc
289
289
  has contracted — asking for more comes back as a 1400-1408.
290
290
  """
291
291
 
292
+ PlanUseData: TypeAlias = _models.PlanUseData
293
+ """What is being CONSUMED right now: a :data:`PlanData` plus ``publications``.
294
+
295
+ ``publications`` is a METRIC, not a quota — publications are unlimited on every plan — so there is
296
+ no ceiling to compare it against: do not draw it as a bar. What throttles whoever publishes too
297
+ much is RATE (a per-hour cap per account, and the per-network daily caps in ``GET /social_limits``),
298
+ not the plan.
299
+
300
+ Not to be confused with :data:`PlanUse`, which is the dashboard's whole block: consumption, what
301
+ was handed down, and the limits together.
302
+ """
303
+
292
304
  Organization: TypeAlias = _models.Organization
293
305
  """An organization: the container of accounts, publications and files. They nest.
294
306
 
@@ -1115,6 +1127,7 @@ __all__ = [
1115
1127
  "PersistentMenu",
1116
1128
  "PlanData",
1117
1129
  "PlanUse",
1130
+ "PlanUseData",
1118
1131
  "PlannerTemplate",
1119
1132
  "PlannerTemplateField",
1120
1133
  "PlannerTemplateFieldType",
@@ -305,7 +305,19 @@ def test_cada_rango_del_catalogo_cae_en_su_clase() -> None:
305
305
  """
306
306
  assert error_class_for_code(500) is error_class_for_code(544)
307
307
  assert error_class_for_code(900).__name__ == "PublicationError"
308
+ # El rango llega hasta 979, no hasta 960: por encima del 960 estan las cuatro ultimas redes
309
+ # (Bluesky, Discord, Telegram y Threads) y los DOS FRENOS DE RITMO que sustituyeron al cupo
310
+ # mensual de publicaciones. Fuera de familia, el 978 llegaba como un error generico y quien lo
311
+ # recibia no tenia forma de saber que la respuesta es esperar.
312
+ assert error_class_for_code(975).__name__ == "PublicationError"
313
+ assert error_class_for_code(978).__name__ == "PublicationError"
314
+ assert error_class_for_code(979).__name__ == "PublicationError"
308
315
  assert error_class_for_code(1300).__name__ == "PlanLimitError"
316
+ # 1308 es el tope de APPS del plan, y 545/546 los dos codigos que trajo abrir la API publica
317
+ # a todos los planes. Los tres nacieron por encima del techo que tenian sus rangos.
318
+ assert error_class_for_code(1308).__name__ == "PlanLimitError"
319
+ assert error_class_for_code(545).__name__ == "AuthError"
320
+ assert error_class_for_code(546).__name__ == "AuthError"
309
321
  assert error_class_for_code(1408).__name__ == "PlanLimitError"
310
322
  assert error_class_for_code(2299).__name__ == "IntegrationError"
311
323
  # Los siete de las plantillas del planificador. No hay nada que registrar para que caigan aqui:
@@ -92,7 +92,7 @@ def _camino(
92
92
  url=f"{BASE_URL}/clients/cli1/organizations?limit=1",
93
93
  json={"organizations": [ORGANIZACION], "total": 1},
94
94
  )
95
- httpx_mock.add_response(url=f"{ORG}/limits", json={"accounts": 5, "publications": 100})
95
+ httpx_mock.add_response(url=f"{ORG}/limits", json={"accounts": 5, "users": 3})
96
96
  httpx_mock.add_response(
97
97
  url=f"{ORG}?getUse=true",
98
98
  json={
@@ -77,7 +77,7 @@ def _camino(httpx_mock: HTTPXMock, *, publicacion: dict[str, Any] | None = None)
77
77
  url=f"{BASE_URL}/clients/cli1/organizations?limit=1",
78
78
  json={"organizations": [ORGANIZACION], "total": 1},
79
79
  )
80
- httpx_mock.add_response(url=f"{ORG}/limits", json={"accounts": 5, "publications": 100})
80
+ httpx_mock.add_response(url=f"{ORG}/limits", json={"accounts": 5, "users": 3})
81
81
  httpx_mock.add_response(
82
82
  url=f"{ORG}?getUse=true",
83
83
  json={"organization": {**ORGANIZACION, "actual_use": {"accounts": 1, "publications": 12}}},
@@ -73,9 +73,9 @@ def test_las_hijas_se_listan_se_iteran_y_se_crean(cliente: ClienteDePrueba, http
73
73
 
74
74
  def test_limits_llega_sin_sobre(cliente: ClienteDePrueba, httpx_mock: HTTPXMock) -> None:
75
75
  """La cascada ya resuelta, y en crudo. Es lo que hay que mirar, no `actual_plan`."""
76
- httpx_mock.add_response(url=f"{ORG}/limits", json={"accounts": 5, "publications": 100})
76
+ httpx_mock.add_response(url=f"{ORG}/limits", json={"accounts": 5, "users": 3})
77
77
 
78
- assert cliente.esperar(cliente.pv.organizations.limits("org1")) == {"accounts": 5, "publications": 100}
78
+ assert cliente.esperar(cliente.pv.organizations.limits("org1")) == {"accounts": 5, "users": 3}
79
79
  assert ruta(unica(httpx_mock)) == "/organizations/org1/limits"
80
80
 
81
81
 
File without changes
File without changes
File without changes
File without changes
File without changes