planvortex 0.6.0__tar.gz → 0.7.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.
- {planvortex-0.6.0 → planvortex-0.7.0}/CHANGELOG.md +25 -0
- {planvortex-0.6.0 → planvortex-0.7.0}/PKG-INFO +4 -4
- {planvortex-0.6.0 → planvortex-0.7.0}/README.md +2 -2
- {planvortex-0.6.0 → planvortex-0.7.0}/openapi/planvortex.openapi.json +17 -34
- {planvortex-0.6.0 → planvortex-0.7.0}/pyproject.toml +2 -2
- {planvortex-0.6.0 → planvortex-0.7.0}/src/planvortex/_generated/models.py +8 -18
- {planvortex-0.6.0 → planvortex-0.7.0}/src/planvortex/_version.py +1 -1
- {planvortex-0.6.0 → planvortex-0.7.0}/.gitignore +0 -0
- {planvortex-0.6.0 → planvortex-0.7.0}/LICENSE +0 -0
- {planvortex-0.6.0 → planvortex-0.7.0}/scripts/build_docs.py +0 -0
- {planvortex-0.6.0 → planvortex-0.7.0}/scripts/check_packaging.py +0 -0
- {planvortex-0.6.0 → planvortex-0.7.0}/scripts/generate_models.py +0 -0
- {planvortex-0.6.0 → planvortex-0.7.0}/scripts/generate_sync.py +0 -0
- {planvortex-0.6.0 → planvortex-0.7.0}/scripts/route_coverage.py +0 -0
- {planvortex-0.6.0 → planvortex-0.7.0}/src/planvortex/__init__.py +0 -0
- {planvortex-0.6.0 → planvortex-0.7.0}/src/planvortex/_client.py +0 -0
- {planvortex-0.6.0 → planvortex-0.7.0}/src/planvortex/_client_sync.py +0 -0
- {planvortex-0.6.0 → planvortex-0.7.0}/src/planvortex/_core/__init__.py +0 -0
- {planvortex-0.6.0 → planvortex-0.7.0}/src/planvortex/_core/auth.py +0 -0
- {planvortex-0.6.0 → planvortex-0.7.0}/src/planvortex/_core/auth_sync.py +0 -0
- {planvortex-0.6.0 → planvortex-0.7.0}/src/planvortex/_core/errors.py +0 -0
- {planvortex-0.6.0 → planvortex-0.7.0}/src/planvortex/_core/files.py +0 -0
- {planvortex-0.6.0 → planvortex-0.7.0}/src/planvortex/_core/http.py +0 -0
- {planvortex-0.6.0 → planvortex-0.7.0}/src/planvortex/_core/http_sync.py +0 -0
- {planvortex-0.6.0 → planvortex-0.7.0}/src/planvortex/_core/pagination.py +0 -0
- {planvortex-0.6.0 → planvortex-0.7.0}/src/planvortex/_core/query.py +0 -0
- {planvortex-0.6.0 → planvortex-0.7.0}/src/planvortex/_core/transport.py +0 -0
- {planvortex-0.6.0 → planvortex-0.7.0}/src/planvortex/_generated/__init__.py +0 -0
- {planvortex-0.6.0 → planvortex-0.7.0}/src/planvortex/_shapes.py +0 -0
- {planvortex-0.6.0 → planvortex-0.7.0}/src/planvortex/py.typed +0 -0
- {planvortex-0.6.0 → planvortex-0.7.0}/src/planvortex/resources/__init__.py +0 -0
- {planvortex-0.6.0 → planvortex-0.7.0}/src/planvortex/resources/accounts.py +0 -0
- {planvortex-0.6.0 → planvortex-0.7.0}/src/planvortex/resources/ai_plans.py +0 -0
- {planvortex-0.6.0 → planvortex-0.7.0}/src/planvortex/resources/apps.py +0 -0
- {planvortex-0.6.0 → planvortex-0.7.0}/src/planvortex/resources/base.py +0 -0
- {planvortex-0.6.0 → planvortex-0.7.0}/src/planvortex/resources/catalog.py +0 -0
- {planvortex-0.6.0 → planvortex-0.7.0}/src/planvortex/resources/clients.py +0 -0
- {planvortex-0.6.0 → planvortex-0.7.0}/src/planvortex/resources/comments.py +0 -0
- {planvortex-0.6.0 → planvortex-0.7.0}/src/planvortex/resources/contacts.py +0 -0
- {planvortex-0.6.0 → planvortex-0.7.0}/src/planvortex/resources/dashboard.py +0 -0
- {planvortex-0.6.0 → planvortex-0.7.0}/src/planvortex/resources/integrations.py +0 -0
- {planvortex-0.6.0 → planvortex-0.7.0}/src/planvortex/resources/messages.py +0 -0
- {planvortex-0.6.0 → planvortex-0.7.0}/src/planvortex/resources/organizations.py +0 -0
- {planvortex-0.6.0 → planvortex-0.7.0}/src/planvortex/resources/products.py +0 -0
- {planvortex-0.6.0 → planvortex-0.7.0}/src/planvortex/resources/publications.py +0 -0
- {planvortex-0.6.0 → planvortex-0.7.0}/src/planvortex/resources/uploads.py +0 -0
- {planvortex-0.6.0 → planvortex-0.7.0}/src/planvortex/resources_sync/__init__.py +0 -0
- {planvortex-0.6.0 → planvortex-0.7.0}/src/planvortex/resources_sync/accounts.py +0 -0
- {planvortex-0.6.0 → planvortex-0.7.0}/src/planvortex/resources_sync/ai_plans.py +0 -0
- {planvortex-0.6.0 → planvortex-0.7.0}/src/planvortex/resources_sync/apps.py +0 -0
- {planvortex-0.6.0 → planvortex-0.7.0}/src/planvortex/resources_sync/base.py +0 -0
- {planvortex-0.6.0 → planvortex-0.7.0}/src/planvortex/resources_sync/catalog.py +0 -0
- {planvortex-0.6.0 → planvortex-0.7.0}/src/planvortex/resources_sync/clients.py +0 -0
- {planvortex-0.6.0 → planvortex-0.7.0}/src/planvortex/resources_sync/comments.py +0 -0
- {planvortex-0.6.0 → planvortex-0.7.0}/src/planvortex/resources_sync/contacts.py +0 -0
- {planvortex-0.6.0 → planvortex-0.7.0}/src/planvortex/resources_sync/dashboard.py +0 -0
- {planvortex-0.6.0 → planvortex-0.7.0}/src/planvortex/resources_sync/integrations.py +0 -0
- {planvortex-0.6.0 → planvortex-0.7.0}/src/planvortex/resources_sync/messages.py +0 -0
- {planvortex-0.6.0 → planvortex-0.7.0}/src/planvortex/resources_sync/organizations.py +0 -0
- {planvortex-0.6.0 → planvortex-0.7.0}/src/planvortex/resources_sync/products.py +0 -0
- {planvortex-0.6.0 → planvortex-0.7.0}/src/planvortex/resources_sync/publications.py +0 -0
- {planvortex-0.6.0 → planvortex-0.7.0}/src/planvortex/resources_sync/uploads.py +0 -0
- {planvortex-0.6.0 → planvortex-0.7.0}/src/planvortex/types.py +0 -0
- {planvortex-0.6.0 → planvortex-0.7.0}/src/planvortex/webhooks.py +0 -0
- {planvortex-0.6.0 → planvortex-0.7.0}/tests/__init__.py +0 -0
- {planvortex-0.6.0 → planvortex-0.7.0}/tests/conftest.py +0 -0
- {planvortex-0.6.0 → planvortex-0.7.0}/tests/contrato.py +0 -0
- {planvortex-0.6.0 → planvortex-0.7.0}/tests/live/__init__.py +0 -0
- {planvortex-0.6.0 → planvortex-0.7.0}/tests/live/conftest.py +0 -0
- {planvortex-0.6.0 → planvortex-0.7.0}/tests/live/test_auth.py +0 -0
- {planvortex-0.6.0 → planvortex-0.7.0}/tests/live/test_catalog.py +0 -0
- {planvortex-0.6.0 → planvortex-0.7.0}/tests/live/test_connect_flow.py +0 -0
- {planvortex-0.6.0 → planvortex-0.7.0}/tests/live/test_errors.py +0 -0
- {planvortex-0.6.0 → planvortex-0.7.0}/tests/live/test_publish.py +0 -0
- {planvortex-0.6.0 → planvortex-0.7.0}/tests/live/test_read.py +0 -0
- {planvortex-0.6.0 → planvortex-0.7.0}/tests/test_accounts.py +0 -0
- {planvortex-0.6.0 → planvortex-0.7.0}/tests/test_ai_plans.py +0 -0
- {planvortex-0.6.0 → planvortex-0.7.0}/tests/test_apps.py +0 -0
- {planvortex-0.6.0 → planvortex-0.7.0}/tests/test_catalog.py +0 -0
- {planvortex-0.6.0 → planvortex-0.7.0}/tests/test_client.py +0 -0
- {planvortex-0.6.0 → planvortex-0.7.0}/tests/test_clients.py +0 -0
- {planvortex-0.6.0 → planvortex-0.7.0}/tests/test_comments.py +0 -0
- {planvortex-0.6.0 → planvortex-0.7.0}/tests/test_contacts.py +0 -0
- {planvortex-0.6.0 → planvortex-0.7.0}/tests/test_core.py +0 -0
- {planvortex-0.6.0 → planvortex-0.7.0}/tests/test_dashboard.py +0 -0
- {planvortex-0.6.0 → planvortex-0.7.0}/tests/test_example_comments.py +0 -0
- {planvortex-0.6.0 → planvortex-0.7.0}/tests/test_example_connect.py +0 -0
- {planvortex-0.6.0 → planvortex-0.7.0}/tests/test_example_publish.py +0 -0
- {planvortex-0.6.0 → planvortex-0.7.0}/tests/test_example_schedule.py +0 -0
- {planvortex-0.6.0 → planvortex-0.7.0}/tests/test_example_webhooks.py +0 -0
- {planvortex-0.6.0 → planvortex-0.7.0}/tests/test_files.py +0 -0
- {planvortex-0.6.0 → planvortex-0.7.0}/tests/test_generate_models.py +0 -0
- {planvortex-0.6.0 → planvortex-0.7.0}/tests/test_generate_sync.py +0 -0
- {planvortex-0.6.0 → planvortex-0.7.0}/tests/test_integrations.py +0 -0
- {planvortex-0.6.0 → planvortex-0.7.0}/tests/test_messages.py +0 -0
- {planvortex-0.6.0 → planvortex-0.7.0}/tests/test_models.py +0 -0
- {planvortex-0.6.0 → planvortex-0.7.0}/tests/test_openapi_freshness.py +0 -0
- {planvortex-0.6.0 → planvortex-0.7.0}/tests/test_organizations.py +0 -0
- {planvortex-0.6.0 → planvortex-0.7.0}/tests/test_packaging.py +0 -0
- {planvortex-0.6.0 → planvortex-0.7.0}/tests/test_pagination.py +0 -0
- {planvortex-0.6.0 → planvortex-0.7.0}/tests/test_products.py +0 -0
- {planvortex-0.6.0 → planvortex-0.7.0}/tests/test_publications.py +0 -0
- {planvortex-0.6.0 → planvortex-0.7.0}/tests/test_query_parity.py +0 -0
- {planvortex-0.6.0 → planvortex-0.7.0}/tests/test_readme.py +0 -0
- {planvortex-0.6.0 → planvortex-0.7.0}/tests/test_route_coverage.py +0 -0
- {planvortex-0.6.0 → planvortex-0.7.0}/tests/test_shapes_parity.py +0 -0
- {planvortex-0.6.0 → planvortex-0.7.0}/tests/test_types.py +0 -0
- {planvortex-0.6.0 → planvortex-0.7.0}/tests/test_uploads.py +0 -0
- {planvortex-0.6.0 → planvortex-0.7.0}/tests/test_webhooks.py +0 -0
|
@@ -4,6 +4,31 @@ 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.7.0] - 2026-09-04
|
|
8
|
+
|
|
9
|
+
**Users are unlimited on every plan**, so the quota that said otherwise is gone from the types —
|
|
10
|
+
the same correction 0.6.0 made for publications, one axis later.
|
|
11
|
+
|
|
12
|
+
It is the second half of a change the server made on 03-09-2026: users stopped being something a
|
|
13
|
+
plan grants and became something you simply count. `PlanData["users"]` was a **required** key, so
|
|
14
|
+
mypy accepted `limits["users"]` and the call raised `KeyError` at runtime, which is the same
|
|
15
|
+
failure mode 0.6.0 describes for publications.
|
|
16
|
+
|
|
17
|
+
### Changed
|
|
18
|
+
|
|
19
|
+
- **BREAKING (types only): `PlanData["users"]` no longer exists.** The count moved to
|
|
20
|
+
`PlanUseData` as an optional key — how many users have access right now, with no limit to
|
|
21
|
+
compare it against, because there is none. Nothing changes on the wire.
|
|
22
|
+
- **The `/apps` operations no longer claim the Custom plan.** Their docstrings still said the API
|
|
23
|
+
needed it. That stopped being true on 02-09-2026, and the docstrings come straight from the
|
|
24
|
+
OpenAPI document, so they were repeating a copy of the spec that had not been rebuilt since.
|
|
25
|
+
|
|
26
|
+
### Removed
|
|
27
|
+
|
|
28
|
+
- **`PlanData["artificial_inteligence"]`, `PlanData["stats"]` and `PlanData["whatsapp"]`.**
|
|
29
|
+
Three optional keys describing a plan model that no longer exists: statistics and WhatsApp are on
|
|
30
|
+
every plan, and what gates AI is `ai_credits` — a plan with credits has AI, and Free has zero.
|
|
31
|
+
|
|
7
32
|
## [0.6.0] - 2026-09-03
|
|
8
33
|
|
|
9
34
|
**Publications are unlimited on every plan**, so the quota that said otherwise is gone from the
|
|
@@ -1,8 +1,8 @@
|
|
|
1
1
|
Metadata-Version: 2.5
|
|
2
2
|
Name: planvortex
|
|
3
|
-
Version: 0.
|
|
3
|
+
Version: 0.7.0
|
|
4
4
|
Summary: Official Python client for the PlanVortex API
|
|
5
|
-
Project-URL: Homepage, https://planvortex.com/developers
|
|
5
|
+
Project-URL: Homepage, https://planvortex.com/en/developers
|
|
6
6
|
Project-URL: Documentation, https://taliasoftworks.github.io/PlanVortexPython/
|
|
7
7
|
Project-URL: API, https://planvortex.com/documentation
|
|
8
8
|
Project-URL: Repository, https://github.com/taliasoftworks/PlanVortexPython
|
|
@@ -53,7 +53,7 @@ pip install planvortex
|
|
|
53
53
|
- Python 3.10 and newer.
|
|
54
54
|
|
|
55
55
|
**Reference:** <https://taliasoftworks.github.io/PlanVortexPython/> · **Guides:**
|
|
56
|
-
[planvortex.com/developers](https://planvortex.com/developers)
|
|
56
|
+
[planvortex.com/en/developers](https://planvortex.com/en/developers)
|
|
57
57
|
|
|
58
58
|
## Authentication
|
|
59
59
|
|
|
@@ -371,7 +371,7 @@ and reaches a person.
|
|
|
371
371
|
|
|
372
372
|
- [Reference](https://taliasoftworks.github.io/PlanVortexPython/)
|
|
373
373
|
- [API documentation](https://planvortex.com/documentation)
|
|
374
|
-
- [Developers](https://planvortex.com/developers)
|
|
374
|
+
- [Developers](https://planvortex.com/en/developers)
|
|
375
375
|
- [Node client](https://github.com/taliasoftworks/PlanVortexNode)
|
|
376
376
|
- [Contributing](https://github.com/taliasoftworks/PlanVortexPython/blob/main/CONTRIBUTING.md) · [Security](https://github.com/taliasoftworks/PlanVortexPython/blob/main/SECURITY.md) · [Changelog](https://github.com/taliasoftworks/PlanVortexPython/blob/main/CHANGELOG.md)
|
|
377
377
|
|
|
@@ -23,7 +23,7 @@ pip install planvortex
|
|
|
23
23
|
- Python 3.10 and newer.
|
|
24
24
|
|
|
25
25
|
**Reference:** <https://taliasoftworks.github.io/PlanVortexPython/> · **Guides:**
|
|
26
|
-
[planvortex.com/developers](https://planvortex.com/developers)
|
|
26
|
+
[planvortex.com/en/developers](https://planvortex.com/en/developers)
|
|
27
27
|
|
|
28
28
|
## Authentication
|
|
29
29
|
|
|
@@ -341,7 +341,7 @@ and reaches a person.
|
|
|
341
341
|
|
|
342
342
|
- [Reference](https://taliasoftworks.github.io/PlanVortexPython/)
|
|
343
343
|
- [API documentation](https://planvortex.com/documentation)
|
|
344
|
-
- [Developers](https://planvortex.com/developers)
|
|
344
|
+
- [Developers](https://planvortex.com/en/developers)
|
|
345
345
|
- [Node client](https://github.com/taliasoftworks/PlanVortexNode)
|
|
346
346
|
- [Contributing](https://github.com/taliasoftworks/PlanVortexPython/blob/main/CONTRIBUTING.md) · [Security](https://github.com/taliasoftworks/PlanVortexPython/blob/main/SECURITY.md) · [Changelog](https://github.com/taliasoftworks/PlanVortexPython/blob/main/CHANGELOG.md)
|
|
347
347
|
|
|
@@ -656,7 +656,7 @@
|
|
|
656
656
|
"accounts"
|
|
657
657
|
],
|
|
658
658
|
"summary": "Complete the connection of a social account",
|
|
659
|
-
"description": "The endpoint the social network sends the user back to after they authorize. It turns the network's callback into one or more PlanVortex accounts.\n\n**You do not build this URL, the network does.** It is the `redirect_uri` inside the link that `GET /organizations/{id_organization}/connect_links` handed out, so the query parameters are whatever the network appends — typically `code` and `state`. Pass them through untouched.\n\n**One authorization can produce several accounts.** A Facebook user with four pages ends up with four; a Discord authorization produces the channel that was picked.\n\n**It answers 200 even when it fails.** The result carries `errorCode` and `errorMsg` instead of an error body, because the browser lands here from a redirect and a raw 400 would be a broken page. Check `errorCode`: empty means everything went well.\n\n**An app cannot call this** — it needs a user token or a temporal connect token (error 519 otherwise). See `GET /organizations/{id_organization}/temporal_connect_token`.\n\n**A temporal connect token is spent here.** Once this call succeeds, that token cannot connect anything else and answers error 543; the `enable` calls that finish the same connection still work until it expires. And if the token was issued for one network, calling this for another answers error 544.\n\n**Telegram does not come through here, and cannot be made to.** That network has no callback: the account is created by PlanVortex when the bot is added to a channel, and what authorizes it is a single-use voucher minted at that moment and spent in the same breath — it never leaves the server, so calling this endpoint for `telegram` answers error 700. It is deliberate: the bot is shared, so without it anyone could hang any channel where that bot is an admin off their own organization by passing a chat id by hand. What an integration listens for instead is the `new_account` webhook notification.",
|
|
659
|
+
"description": "The endpoint the social network sends the user back to after they authorize. It turns the network's callback into one or more PlanVortex accounts.\n\n**You do not build this URL, the network does.** It is the `redirect_uri` inside the link that `GET /organizations/{id_organization}/connect_links` handed out, so the query parameters are whatever the network appends — typically `code` and `state`. Pass them through untouched.\n\n**One authorization can produce several accounts.** A Facebook user with four pages ends up with four; a Discord authorization produces the channel that was picked.\n\n**It answers 200 even when it fails.** The result carries `errorCode` and `errorMsg` instead of an error body, because the browser lands here from a redirect and a raw 400 would be a broken page. Check `errorCode`: empty means everything went well.\n\n**An app cannot call this** — it needs a user token or a temporal connect token (error 519 otherwise). See `GET /organizations/{id_organization}/temporal_connect_token`.\n\n**A temporal connect token is spent here.** Once this call succeeds, that token cannot connect anything else and answers error 543; the `enable` calls that finish the same connection still work until it expires. And if the token was issued for one network, calling this for another answers error 544.\n\n**Telegram does not come through here, and cannot be made to.** That network has no callback: the account is created by PlanVortex when the bot is added to a channel, and what authorizes it is a single-use voucher minted at that moment and spent in the same breath — it never leaves the server, so calling this endpoint for `telegram` answers error 700. It is deliberate: the bot is shared, so without it anyone could hang any channel where that bot is an admin off their own organization by passing a chat id by hand. What an integration listens for instead is the `new_account` webhook notification.\n\n**And a Telegram account arrives already active.** In the other eleven networks this endpoint hands you accounts that are still off, and you pick which ones spend a plan slot with `POST .../accounts/{id_account}/enable`. Here there is nothing to call: one channel arrives — the one the person picked in Telegram — and PlanVortex takes the slot for it right then. If the plan has no free slot the account is still created, off, and the person is told so in the bot chat; `GET /organizations/{id_organization}/accounts` will not list it, because that listing only returns active accounts, but naming it in the `accounts` filter does return it.",
|
|
660
660
|
"operationId": "connectAccount",
|
|
661
661
|
"x-planvortex-identity": [
|
|
662
662
|
"current_user",
|
|
@@ -736,7 +736,7 @@
|
|
|
736
736
|
"accounts"
|
|
737
737
|
],
|
|
738
738
|
"summary": "Re-enable a disconnected account",
|
|
739
|
-
"description": "Brings back an account that was removed, without going through the authorization again — as long as its stored token still works.\n\nIt takes a **slot of the plan**, so it fails with error 706 when the organization is already at its account limit. It also turns the network's webhooks back on, on any plan but the free one.",
|
|
739
|
+
"description": "Brings back an account that was removed, without going through the authorization again — as long as its stored token still works.\n\nIt takes a **slot of the plan**, so it fails with error 706 when the organization is already at its account limit. It also turns the network's webhooks back on, on any plan but the free one.\n\n**On `telegram` you normally never call this.** That network's accounts are born active — there is no authorization return in which to choose them, so PlanVortex takes the slot when the bot is added to the channel. The one case left is a channel that arrived while the plan was full: it stays off until a slot frees up, and this is what turns it on.",
|
|
740
740
|
"operationId": "enableAccount",
|
|
741
741
|
"x-planvortex-identity": [
|
|
742
742
|
"current_user",
|
|
@@ -945,7 +945,7 @@
|
|
|
945
945
|
"ai_plans"
|
|
946
946
|
],
|
|
947
947
|
"summary": "Create an AI publication plan",
|
|
948
|
-
"description": "Create a weekly AI publication plan. The plan is queued in state 'pending' and generated asynchronously by the generate-ai-plans job; poll GET by id while state is pending or generating. Validations at creation: the client plan allows AI (
|
|
948
|
+
"description": "Create a weekly AI publication plan. The plan is queued in state 'pending' and generated asynchronously by the generate-ai-plans job; poll GET by id while state is pending or generating. Validations at creation: the client plan allows AI (it contracts AI credits; Free does not), the accounts belong to the organization, there are enough AI credits for the deterministic base cost (orchestration + target texts), and there is room in the organization's monthly publication limit. Returns the created plan together with the deterministic cost estimate. Requires the ai_plans:create permission. Since the plan can be generated from a SOURCE (`template` + `source`), part of that validation is the source itself: the article is downloaded, the catalogue is read live and the product pictures are copied — all of it inside this request, so what does not work fails with the user in front of it. A plan whose source did not fit in the week is still created, and says so in ai_plan.warnings (2117).",
|
|
949
949
|
"operationId": "addAiPlan",
|
|
950
950
|
"x-planvortex-identity": [
|
|
951
951
|
"current_user",
|
|
@@ -1564,7 +1564,7 @@
|
|
|
1564
1564
|
"apps"
|
|
1565
1565
|
],
|
|
1566
1566
|
"summary": "List the client's apps",
|
|
1567
|
-
"description": "The apps of a client. Secrets never travel here: to read one, ask for it explicitly with the `secret` endpoint.\n\n**Needs a USER token, not an app token.** With client credentials it answers `ERROR_CODE_512`.
|
|
1567
|
+
"description": "The apps of a client. Secrets never travel here: to read one, ask for it explicitly with the `secret` endpoint.\n\n**Needs a USER token, not an app token.** With client credentials it answers `ERROR_CODE_512`. **No plan is required**: apps are on all four plans, the free one included — what the plan decides is how many you can hold at once and how fast you can call.",
|
|
1568
1568
|
"operationId": "getClientApps",
|
|
1569
1569
|
"x-planvortex-identity": [
|
|
1570
1570
|
"current_user"
|
|
@@ -1616,7 +1616,7 @@
|
|
|
1616
1616
|
"apps"
|
|
1617
1617
|
],
|
|
1618
1618
|
"summary": "Create an app",
|
|
1619
|
-
"description": "Creates the app and its credentials.\n\n**
|
|
1619
|
+
"description": "Creates the app and its credentials.\n\n**How many apps fit is decided by the plan**, not by a fixed limit of one: 1 on free, 2 on basic, 5 on pro and 10 on custom. Going over answers `ERROR_CODE_1308`, which carries `limit` and `used` in its `data`. Deleting an app frees its slot immediately, because only live apps count. If you just need to rotate credentials, update the existing app rather than creating another.\n\n**The user's email must be verified** (`ERROR_CODE_546`). This is the only route in `/apps` that asks for it: an app is a key to the whole public API, and this is what stops a throwaway address from minting them in bulk. Reading, updating and deleting an app you already own do not check it.\n\nEvery URL is validated: a bad entry in `allowed_domains` answers 531, in `redirect_urls` 532, and a bad `webhook_url` answers 535.\n\n**The secret is not in the response.** Read it with `GET /clients/{id_client}/apps/{id_app}/secret`.\n\n**Needs a USER token, not an app token.** With client credentials it answers `ERROR_CODE_512`. **No plan is required**: apps are on all four plans, the free one included — what the plan decides is how many you can hold at once and how fast you can call.",
|
|
1620
1620
|
"operationId": "createClientApp",
|
|
1621
1621
|
"x-planvortex-identity": [
|
|
1622
1622
|
"current_user"
|
|
@@ -1667,7 +1667,7 @@
|
|
|
1667
1667
|
"apps"
|
|
1668
1668
|
],
|
|
1669
1669
|
"summary": "Get one app",
|
|
1670
|
-
"description": "The app's record, without its secret. Note the envelope: `{client_app}` here, `{client_apps, total}` in the list.\n\n**An app token works here.** This is the one part of `/apps` that does: an app can read and update its own record, but it cannot list, create, delete, or read the secret.
|
|
1670
|
+
"description": "The app's record, without its secret. Note the envelope: `{client_app}` here, `{client_apps, total}` in the list.\n\n**An app token works here.** This is the one part of `/apps` that does: an app can read and update its own record, but it cannot list, create, delete, or read the secret. **No plan is required**: apps are on all four plans, the free one included.",
|
|
1671
1671
|
"operationId": "getClientApp",
|
|
1672
1672
|
"x-planvortex-identity": [
|
|
1673
1673
|
"current_user",
|
|
@@ -1710,7 +1710,7 @@
|
|
|
1710
1710
|
"apps"
|
|
1711
1711
|
],
|
|
1712
1712
|
"summary": "Update an app",
|
|
1713
|
-
"description": "Replaces the app's configuration. **Every field is overwritten with what you send**, so a `redirect_urls` you leave out becomes an empty list — send the whole object, not a patch.\n\nChanging `webhook_url` takes effect on the next event; there is no verification handshake.\n\n**It replaces every field with what the body carries.** `name`, `keycloak_client_idenfifier`, `allowed_domains`, `redirect_urls` and `webhook_url` are all written as sent, so omitting one erases it — sending an update without `webhook_url` turns the webhook off. Read the app first and send it back whole.\n\n**An app token works here.** This is the one part of `/apps` that does: an app can read and update its own record, but it cannot list, create, delete, or read the secret.
|
|
1713
|
+
"description": "Replaces the app's configuration. **Every field is overwritten with what you send**, so a `redirect_urls` you leave out becomes an empty list — send the whole object, not a patch.\n\nChanging `webhook_url` takes effect on the next event; there is no verification handshake.\n\n**It replaces every field with what the body carries.** `name`, `keycloak_client_idenfifier`, `allowed_domains`, `redirect_urls` and `webhook_url` are all written as sent, so omitting one erases it — sending an update without `webhook_url` turns the webhook off. Read the app first and send it back whole.\n\n**An app token works here.** This is the one part of `/apps` that does: an app can read and update its own record, but it cannot list, create, delete, or read the secret. **No plan is required**: apps are on all four plans, the free one included.",
|
|
1714
1714
|
"operationId": "updateClientApp",
|
|
1715
1715
|
"x-planvortex-identity": [
|
|
1716
1716
|
"current_user",
|
|
@@ -1763,7 +1763,7 @@
|
|
|
1763
1763
|
"apps"
|
|
1764
1764
|
],
|
|
1765
1765
|
"summary": "Delete an app",
|
|
1766
|
-
"description": "Revokes the credentials and marks the app as deleted. **Tokens already issued stop working**: the token endpoint checks that the app still exists before answering, and requests carrying an old token no longer resolve to an app.\n\n**Needs a USER token, not an app token.** With client credentials it answers `ERROR_CODE_512`.
|
|
1766
|
+
"description": "Revokes the credentials and marks the app as deleted. **Tokens already issued stop working**: the token endpoint checks that the app still exists before answering, and requests carrying an old token no longer resolve to an app.\n\n**Needs a USER token, not an app token.** With client credentials it answers `ERROR_CODE_512`. **No plan is required**: apps are on all four plans, the free one included — what the plan decides is how many you can hold at once and how fast you can call.",
|
|
1767
1767
|
"operationId": "deleteClientApp",
|
|
1768
1768
|
"x-planvortex-identity": [
|
|
1769
1769
|
"current_user"
|
|
@@ -1799,7 +1799,7 @@
|
|
|
1799
1799
|
"apps"
|
|
1800
1800
|
],
|
|
1801
1801
|
"summary": "Read the app's secret",
|
|
1802
|
-
"description": "Returns the `client_secret` in clear. It is a separate endpoint precisely so that the secret never travels inside a listing that gets logged, cached or drawn on a screen by accident.\n\nThe same secret is what signs the outgoing webhooks (`x-hub-signature`, `x-hub-signature-256`).\n\n**Needs a USER token, not an app token.** With client credentials it answers `ERROR_CODE_512`.
|
|
1802
|
+
"description": "Returns the `client_secret` in clear. It is a separate endpoint precisely so that the secret never travels inside a listing that gets logged, cached or drawn on a screen by accident.\n\nThe same secret is what signs the outgoing webhooks (`x-hub-signature`, `x-hub-signature-256`).\n\n**Needs a USER token, not an app token.** With client credentials it answers `ERROR_CODE_512`. **No plan is required**: apps are on all four plans, the free one included — what the plan decides is how many you can hold at once and how fast you can call.\n\nIt comes straight from Keycloak, so it is the live secret and not a copy: treat the response as a credential and never log it.",
|
|
1803
1803
|
"operationId": "getClientAppSecret",
|
|
1804
1804
|
"x-planvortex-identity": [
|
|
1805
1805
|
"current_user"
|
|
@@ -2449,7 +2449,6 @@
|
|
|
2449
2449
|
"name": "Client X",
|
|
2450
2450
|
"actual_plan": {
|
|
2451
2451
|
"accounts": 4,
|
|
2452
|
-
"users": 1,
|
|
2453
2452
|
"space": 0.25,
|
|
2454
2453
|
"integrations": 1,
|
|
2455
2454
|
"twitter_credits": 2500,
|
|
@@ -2624,7 +2623,6 @@
|
|
|
2624
2623
|
"value": {
|
|
2625
2624
|
"actual_plan": {
|
|
2626
2625
|
"accounts": 8,
|
|
2627
|
-
"users": 3,
|
|
2628
2626
|
"space": 1,
|
|
2629
2627
|
"integrations": 3,
|
|
2630
2628
|
"twitter_credits": 5000,
|
|
@@ -2709,7 +2707,7 @@
|
|
|
2709
2707
|
"clients"
|
|
2710
2708
|
],
|
|
2711
2709
|
"summary": "Configure the client's own AI models (BYOK)",
|
|
2712
|
-
"description": "Set the client's Bring-Your-Own-Key AI provider configuration per scope. The request body is an object keyed by scope (orchestrator, text, image); a scope set to null clears it. Providers and capabilities are validated against a fixed catalogue. When a scope uses BYOK, its calls are billed to the client's own provider and do NOT consume PlanVortex AI credits, but the
|
|
2710
|
+
"description": "Set the client's Bring-Your-Own-Key AI provider configuration per scope. The request body is an object keyed by scope (orchestrator, text, image); a scope set to null clears it. Providers and capabilities are validated against a fixed catalogue. When a scope uses BYOK, its calls are billed to the client's own provider and do NOT consume PlanVortex AI credits, but the plan still has to include AI credits to use the feature. API keys are stored encrypted and never returned; the response client only exposes provider, model and has_api_key per scope. Requires the client:update permission.",
|
|
2713
2711
|
"operationId": "updateAiSettings",
|
|
2714
2712
|
"x-planvortex-identity": [
|
|
2715
2713
|
"current_user",
|
|
@@ -6022,7 +6020,6 @@
|
|
|
6022
6020
|
"value": {
|
|
6023
6021
|
"actual_plan": {
|
|
6024
6022
|
"accounts": 8,
|
|
6025
|
-
"users": 3,
|
|
6026
6023
|
"space": 1,
|
|
6027
6024
|
"integrations": 3,
|
|
6028
6025
|
"twitter_credits": 5000,
|
|
@@ -6340,7 +6337,6 @@
|
|
|
6340
6337
|
"name": "Client X - Spain",
|
|
6341
6338
|
"actual_plan": {
|
|
6342
6339
|
"accounts": 3,
|
|
6343
|
-
"users": 1,
|
|
6344
6340
|
"space": 0.25,
|
|
6345
6341
|
"integrations": 0,
|
|
6346
6342
|
"twitter_credits": 0,
|
|
@@ -13378,10 +13374,9 @@
|
|
|
13378
13374
|
},
|
|
13379
13375
|
"PlanData": {
|
|
13380
13376
|
"type": "object",
|
|
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.",
|
|
13377
|
+
"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.\n\nUsers are NOT here: every plan has unlimited users, so they are neither charged nor split between organizations. They are still counted, in `PlanUseData.users`. There are no `artificial_inteligence`, `whatsapp` or `stats` flags either: statistics and WhatsApp are on every plan, and what gates AI is `ai_credits` — a plan with credits has AI, and Free has zero.",
|
|
13382
13378
|
"required": [
|
|
13383
13379
|
"accounts",
|
|
13384
|
-
"users",
|
|
13385
13380
|
"space",
|
|
13386
13381
|
"integrations"
|
|
13387
13382
|
],
|
|
@@ -13390,10 +13385,6 @@
|
|
|
13390
13385
|
"type": "integer",
|
|
13391
13386
|
"description": "Social accounts that may be connected."
|
|
13392
13387
|
},
|
|
13393
|
-
"users": {
|
|
13394
|
-
"type": "integer",
|
|
13395
|
-
"description": "Users with access."
|
|
13396
|
-
},
|
|
13397
13388
|
"space": {
|
|
13398
13389
|
"type": "number",
|
|
13399
13390
|
"description": "Storage, in GB."
|
|
@@ -13404,7 +13395,7 @@
|
|
|
13404
13395
|
},
|
|
13405
13396
|
"apps": {
|
|
13406
13397
|
"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
|
|
13398
|
+
"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 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."
|
|
13408
13399
|
},
|
|
13409
13400
|
"twitter_credits": {
|
|
13410
13401
|
"type": "integer",
|
|
@@ -13413,24 +13404,12 @@
|
|
|
13413
13404
|
"ai_credits": {
|
|
13414
13405
|
"type": "integer",
|
|
13415
13406
|
"description": "Monthly AI credits (1 credit = $0.001 of provider cost): 15 per plan orchestration pass, 2 per generated text, 70 per generated image. Resets monthly and does not roll over. A client using its own provider key (BYOK) does not consume them in that scope."
|
|
13416
|
-
},
|
|
13417
|
-
"artificial_inteligence": {
|
|
13418
|
-
"type": "boolean",
|
|
13419
|
-
"description": "Whether AI generation is enabled."
|
|
13420
|
-
},
|
|
13421
|
-
"whatsapp": {
|
|
13422
|
-
"type": "boolean",
|
|
13423
|
-
"description": "Whether WhatsApp may be connected."
|
|
13424
|
-
},
|
|
13425
|
-
"stats": {
|
|
13426
|
-
"type": "boolean",
|
|
13427
|
-
"description": "Whether statistics collection is enabled."
|
|
13428
13407
|
}
|
|
13429
13408
|
}
|
|
13430
13409
|
},
|
|
13431
13410
|
"PlanUseData": {
|
|
13432
13411
|
"type": "object",
|
|
13433
|
-
"description": "What is being consumed right now. Same shape as PlanData plus `publications`, which
|
|
13412
|
+
"description": "What is being consumed right now. Same shape as PlanData plus `publications` and `users`, which are counted but neither charged nor split between organizations.",
|
|
13434
13413
|
"allOf": [
|
|
13435
13414
|
{
|
|
13436
13415
|
"$ref": "#/components/schemas/PlanData"
|
|
@@ -13440,6 +13419,10 @@
|
|
|
13440
13419
|
"publications": {
|
|
13441
13420
|
"type": "integer",
|
|
13442
13421
|
"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."
|
|
13422
|
+
},
|
|
13423
|
+
"users": {
|
|
13424
|
+
"type": "integer",
|
|
13425
|
+
"description": "Users with access right now. A METRIC, not a quota: users are unlimited on every plan, so there is no limit to compare it against."
|
|
13443
13426
|
}
|
|
13444
13427
|
}
|
|
13445
13428
|
},
|
|
@@ -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.
|
|
12
|
+
version = "0.7.0"
|
|
13
13
|
description = "Official Python client for the PlanVortex API"
|
|
14
14
|
readme = "README.md"
|
|
15
15
|
requires-python = ">=3.10"
|
|
@@ -67,7 +67,7 @@ dependencies = [
|
|
|
67
67
|
]
|
|
68
68
|
|
|
69
69
|
[project.urls]
|
|
70
|
-
Homepage = "https://planvortex.com/developers"
|
|
70
|
+
Homepage = "https://planvortex.com/en/developers"
|
|
71
71
|
# `Documentation` es la referencia generada de ESTE paquete, que es lo que busca quien llega a la
|
|
72
72
|
# ficha de PyPI. La especificacion de la API entera vive aparte, en `API`: son dos cosas distintas y
|
|
73
73
|
# apuntar las dos al mismo sitio dejaba una de las dos sin enlace.
|
|
@@ -1547,16 +1547,14 @@ class OrganizationsUserList(TypedDict):
|
|
|
1547
1547
|
class PlanData(TypedDict):
|
|
1548
1548
|
"""
|
|
1549
1549
|
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.
|
|
1550
|
+
|
|
1551
|
+
Users are NOT here: every plan has unlimited users, so they are neither charged nor split between organizations. They are still counted, in `PlanUseData.users`. There are no `artificial_inteligence`, `whatsapp` or `stats` flags either: statistics and WhatsApp are on every plan, and what gates AI is `ai_credits` — a plan with credits has AI, and Free has zero.
|
|
1550
1552
|
"""
|
|
1551
1553
|
|
|
1552
1554
|
accounts: int
|
|
1553
1555
|
"""
|
|
1554
1556
|
Social accounts that may be connected.
|
|
1555
1557
|
"""
|
|
1556
|
-
users: int
|
|
1557
|
-
"""
|
|
1558
|
-
Users with access.
|
|
1559
|
-
"""
|
|
1560
1558
|
space: float
|
|
1561
1559
|
"""
|
|
1562
1560
|
Storage, in GB.
|
|
@@ -1567,7 +1565,7 @@ class PlanData(TypedDict):
|
|
|
1567
1565
|
"""
|
|
1568
1566
|
apps: NotRequired[int]
|
|
1569
1567
|
"""
|
|
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
|
|
1568
|
+
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 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
1569
|
"""
|
|
1572
1570
|
twitter_credits: NotRequired[int]
|
|
1573
1571
|
"""
|
|
@@ -1577,29 +1575,21 @@ class PlanData(TypedDict):
|
|
|
1577
1575
|
"""
|
|
1578
1576
|
Monthly AI credits (1 credit = $0.001 of provider cost): 15 per plan orchestration pass, 2 per generated text, 70 per generated image. Resets monthly and does not roll over. A client using its own provider key (BYOK) does not consume them in that scope.
|
|
1579
1577
|
"""
|
|
1580
|
-
artificial_inteligence: NotRequired[bool]
|
|
1581
|
-
"""
|
|
1582
|
-
Whether AI generation is enabled.
|
|
1583
|
-
"""
|
|
1584
|
-
whatsapp: NotRequired[bool]
|
|
1585
|
-
"""
|
|
1586
|
-
Whether WhatsApp may be connected.
|
|
1587
|
-
"""
|
|
1588
|
-
stats: NotRequired[bool]
|
|
1589
|
-
"""
|
|
1590
|
-
Whether statistics collection is enabled.
|
|
1591
|
-
"""
|
|
1592
1578
|
|
|
1593
1579
|
|
|
1594
1580
|
class PlanUseData(PlanData):
|
|
1595
1581
|
"""
|
|
1596
|
-
What is being consumed right now. Same shape as PlanData plus `publications`, which
|
|
1582
|
+
What is being consumed right now. Same shape as PlanData plus `publications` and `users`, which are counted but neither charged nor split between organizations.
|
|
1597
1583
|
"""
|
|
1598
1584
|
|
|
1599
1585
|
publications: NotRequired[int]
|
|
1600
1586
|
"""
|
|
1601
1587
|
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
1588
|
"""
|
|
1589
|
+
users: NotRequired[int]
|
|
1590
|
+
"""
|
|
1591
|
+
Users with access right now. A METRIC, not a quota: users are unlimited on every plan, so there is no limit to compare it against.
|
|
1592
|
+
"""
|
|
1603
1593
|
|
|
1604
1594
|
|
|
1605
1595
|
class ProductsProductCatalog(TypedDict):
|
|
@@ -12,7 +12,7 @@ from __future__ import annotations
|
|
|
12
12
|
|
|
13
13
|
import platform
|
|
14
14
|
|
|
15
|
-
VERSION = "0.
|
|
15
|
+
VERSION = "0.7.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.
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|