postdom 0.1.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.
- postdom-0.1.0/.gitignore +8 -0
- postdom-0.1.0/PKG-INFO +113 -0
- postdom-0.1.0/README.md +90 -0
- postdom-0.1.0/pyproject.toml +68 -0
- postdom-0.1.0/src/postdom/__init__.py +138 -0
- postdom-0.1.0/src/postdom/_client.py +1060 -0
- postdom-0.1.0/src/postdom/_contract.py +188 -0
- postdom-0.1.0/src/postdom/_errors.py +151 -0
- postdom-0.1.0/src/postdom/_models.py +257 -0
- postdom-0.1.0/src/postdom/py.typed +1 -0
- postdom-0.1.0/tests/test_async_client.py +113 -0
- postdom-0.1.0/tests/test_client.py +248 -0
- postdom-0.1.0/tests/test_contract.py +112 -0
postdom-0.1.0/.gitignore
ADDED
postdom-0.1.0/PKG-INFO
ADDED
|
@@ -0,0 +1,113 @@
|
|
|
1
|
+
Metadata-Version: 2.4
|
|
2
|
+
Name: postdom
|
|
3
|
+
Version: 0.1.0
|
|
4
|
+
Summary: Typed sync and async Python client for the Postdom short-form publishing API.
|
|
5
|
+
Project-URL: Homepage, https://postdom.com
|
|
6
|
+
Project-URL: Repository, https://github.com/deanfankhauser/postdom
|
|
7
|
+
Project-URL: Issues, https://github.com/deanfankhauser/postdom/issues
|
|
8
|
+
Author-email: Postdom <support@postdom.com>
|
|
9
|
+
License: Proprietary
|
|
10
|
+
Keywords: ai agent,instagram reels api,schedule posts api,short-form video,social media api,tiktok api,youtube shorts api
|
|
11
|
+
Classifier: Development Status :: 3 - Alpha
|
|
12
|
+
Classifier: Programming Language :: Python :: 3
|
|
13
|
+
Classifier: Programming Language :: Python :: 3.10
|
|
14
|
+
Classifier: Programming Language :: Python :: 3.11
|
|
15
|
+
Classifier: Programming Language :: Python :: 3.12
|
|
16
|
+
Classifier: Programming Language :: Python :: 3.13
|
|
17
|
+
Classifier: Programming Language :: Python :: 3.14
|
|
18
|
+
Classifier: Typing :: Typed
|
|
19
|
+
Requires-Python: >=3.10
|
|
20
|
+
Requires-Dist: httpx<1,>=0.27
|
|
21
|
+
Requires-Dist: pydantic<3,>=2.7
|
|
22
|
+
Description-Content-Type: text/markdown
|
|
23
|
+
|
|
24
|
+
# postdom
|
|
25
|
+
|
|
26
|
+
Typed synchronous and asynchronous Python clients for the [Postdom](https://postdom.com)
|
|
27
|
+
short-form publishing API.
|
|
28
|
+
|
|
29
|
+
```bash
|
|
30
|
+
pip install postdom
|
|
31
|
+
```
|
|
32
|
+
|
|
33
|
+
```python
|
|
34
|
+
from pathlib import Path
|
|
35
|
+
import os
|
|
36
|
+
|
|
37
|
+
from postdom import Postdom
|
|
38
|
+
|
|
39
|
+
with Postdom(os.environ["POSTDOM_API_KEY"]) as postdom:
|
|
40
|
+
upload = postdom.upload_media(
|
|
41
|
+
Path("launch.mp4").read_bytes(),
|
|
42
|
+
content_type="video/mp4",
|
|
43
|
+
platforms=["tiktok"],
|
|
44
|
+
)
|
|
45
|
+
media = postdom.wait_for_media(upload.media_handle)
|
|
46
|
+
if media.status != "stored":
|
|
47
|
+
raise RuntimeError(f"Media is {media.status}")
|
|
48
|
+
|
|
49
|
+
submission = postdom.publish_video(
|
|
50
|
+
account_ids=["account-tiktok"],
|
|
51
|
+
media_handle=media.media_handle,
|
|
52
|
+
caption="Launch day. Here is what we shipped.",
|
|
53
|
+
intent="Announce the launch",
|
|
54
|
+
)
|
|
55
|
+
result = postdom.wait_for_publish(submission.id)
|
|
56
|
+
print(result.status)
|
|
57
|
+
```
|
|
58
|
+
|
|
59
|
+
The async client has the same Python operation names:
|
|
60
|
+
|
|
61
|
+
```python
|
|
62
|
+
import os
|
|
63
|
+
|
|
64
|
+
from postdom import AsyncPostdom
|
|
65
|
+
|
|
66
|
+
|
|
67
|
+
async def workspace_status():
|
|
68
|
+
async with AsyncPostdom(os.environ["POSTDOM_API_KEY"]) as postdom:
|
|
69
|
+
return await postdom.get_workspace_status()
|
|
70
|
+
```
|
|
71
|
+
|
|
72
|
+
## Contract
|
|
73
|
+
|
|
74
|
+
The package mirrors the 14 agent-authority operations in `@postdom/sdk`:
|
|
75
|
+
|
|
76
|
+
- `get_workspace_status`, `list_accounts`, `connect_account`
|
|
77
|
+
- `create_media_upload`, `get_media_status`, `upload_media`, `wait_for_media`
|
|
78
|
+
- `submit_plan`, `get_plan`
|
|
79
|
+
- `publish_video`, `get_post`, `wait_for_publish`
|
|
80
|
+
- `get_post_performance`, `get_account_performance`, `get_best_posts`
|
|
81
|
+
- `get_brief`, `get_digest`
|
|
82
|
+
|
|
83
|
+
Responses are Pydantic v2 models. Models allow additive server fields so a harmless API addition
|
|
84
|
+
does not break existing applications. The checked-in contract suite locks operation paths,
|
|
85
|
+
platforms, statuses, metrics, limits, and media handles to the audited Node SDK, which is itself
|
|
86
|
+
drift-tested against the live API route table and `@postdom/core`.
|
|
87
|
+
|
|
88
|
+
## Authentication and safety
|
|
89
|
+
|
|
90
|
+
Use a workspace `pd_live_` API key or `pd_oauth_` OAuth access token. Both carry agent authority:
|
|
91
|
+
approval, billing, and dashboard administration remain human-only and return
|
|
92
|
+
`403 agent_token_forbidden` by design. Credentials are held as Pydantic `SecretStr` values and
|
|
93
|
+
never appear in client representations or SDK-generated errors.
|
|
94
|
+
|
|
95
|
+
`publish_video` defaults TikTok to `SELF_ONLY`, Instagram to a Reel with AI disclosure, and YouTube
|
|
96
|
+
to private with synthetic-media disclosure. It generates one idempotency key and reuses it across
|
|
97
|
+
safe retries. `connect_account` is never retried because it has no idempotency key.
|
|
98
|
+
|
|
99
|
+
`upload_media` sends bytes through a separate HTTP client directly to the short-lived signed
|
|
100
|
+
storage URL. The Postdom bearer credential is never attached to that PUT, the PUT is never
|
|
101
|
+
retried, and the signed URL is not returned from the high-level result.
|
|
102
|
+
|
|
103
|
+
## Errors
|
|
104
|
+
|
|
105
|
+
Non-2xx responses raise typed subclasses of `PostdomAPIError`, including
|
|
106
|
+
`PostdomAuthenticationError`, `PostdomScopeError`, `PostdomHumanRouteError`,
|
|
107
|
+
`PostdomBillingError`, `PostdomRateLimitError`, and `PostdomServerError`. Network and deadline
|
|
108
|
+
failures use `PostdomConnectionError` and `PostdomTimeoutError`.
|
|
109
|
+
|
|
110
|
+
## Release status
|
|
111
|
+
|
|
112
|
+
Version `0.1.0` is the initial package version. PyPI publishing is held until Dean explicitly
|
|
113
|
+
authorizes it; building and audit do not imply registry publication.
|
postdom-0.1.0/README.md
ADDED
|
@@ -0,0 +1,90 @@
|
|
|
1
|
+
# postdom
|
|
2
|
+
|
|
3
|
+
Typed synchronous and asynchronous Python clients for the [Postdom](https://postdom.com)
|
|
4
|
+
short-form publishing API.
|
|
5
|
+
|
|
6
|
+
```bash
|
|
7
|
+
pip install postdom
|
|
8
|
+
```
|
|
9
|
+
|
|
10
|
+
```python
|
|
11
|
+
from pathlib import Path
|
|
12
|
+
import os
|
|
13
|
+
|
|
14
|
+
from postdom import Postdom
|
|
15
|
+
|
|
16
|
+
with Postdom(os.environ["POSTDOM_API_KEY"]) as postdom:
|
|
17
|
+
upload = postdom.upload_media(
|
|
18
|
+
Path("launch.mp4").read_bytes(),
|
|
19
|
+
content_type="video/mp4",
|
|
20
|
+
platforms=["tiktok"],
|
|
21
|
+
)
|
|
22
|
+
media = postdom.wait_for_media(upload.media_handle)
|
|
23
|
+
if media.status != "stored":
|
|
24
|
+
raise RuntimeError(f"Media is {media.status}")
|
|
25
|
+
|
|
26
|
+
submission = postdom.publish_video(
|
|
27
|
+
account_ids=["account-tiktok"],
|
|
28
|
+
media_handle=media.media_handle,
|
|
29
|
+
caption="Launch day. Here is what we shipped.",
|
|
30
|
+
intent="Announce the launch",
|
|
31
|
+
)
|
|
32
|
+
result = postdom.wait_for_publish(submission.id)
|
|
33
|
+
print(result.status)
|
|
34
|
+
```
|
|
35
|
+
|
|
36
|
+
The async client has the same Python operation names:
|
|
37
|
+
|
|
38
|
+
```python
|
|
39
|
+
import os
|
|
40
|
+
|
|
41
|
+
from postdom import AsyncPostdom
|
|
42
|
+
|
|
43
|
+
|
|
44
|
+
async def workspace_status():
|
|
45
|
+
async with AsyncPostdom(os.environ["POSTDOM_API_KEY"]) as postdom:
|
|
46
|
+
return await postdom.get_workspace_status()
|
|
47
|
+
```
|
|
48
|
+
|
|
49
|
+
## Contract
|
|
50
|
+
|
|
51
|
+
The package mirrors the 14 agent-authority operations in `@postdom/sdk`:
|
|
52
|
+
|
|
53
|
+
- `get_workspace_status`, `list_accounts`, `connect_account`
|
|
54
|
+
- `create_media_upload`, `get_media_status`, `upload_media`, `wait_for_media`
|
|
55
|
+
- `submit_plan`, `get_plan`
|
|
56
|
+
- `publish_video`, `get_post`, `wait_for_publish`
|
|
57
|
+
- `get_post_performance`, `get_account_performance`, `get_best_posts`
|
|
58
|
+
- `get_brief`, `get_digest`
|
|
59
|
+
|
|
60
|
+
Responses are Pydantic v2 models. Models allow additive server fields so a harmless API addition
|
|
61
|
+
does not break existing applications. The checked-in contract suite locks operation paths,
|
|
62
|
+
platforms, statuses, metrics, limits, and media handles to the audited Node SDK, which is itself
|
|
63
|
+
drift-tested against the live API route table and `@postdom/core`.
|
|
64
|
+
|
|
65
|
+
## Authentication and safety
|
|
66
|
+
|
|
67
|
+
Use a workspace `pd_live_` API key or `pd_oauth_` OAuth access token. Both carry agent authority:
|
|
68
|
+
approval, billing, and dashboard administration remain human-only and return
|
|
69
|
+
`403 agent_token_forbidden` by design. Credentials are held as Pydantic `SecretStr` values and
|
|
70
|
+
never appear in client representations or SDK-generated errors.
|
|
71
|
+
|
|
72
|
+
`publish_video` defaults TikTok to `SELF_ONLY`, Instagram to a Reel with AI disclosure, and YouTube
|
|
73
|
+
to private with synthetic-media disclosure. It generates one idempotency key and reuses it across
|
|
74
|
+
safe retries. `connect_account` is never retried because it has no idempotency key.
|
|
75
|
+
|
|
76
|
+
`upload_media` sends bytes through a separate HTTP client directly to the short-lived signed
|
|
77
|
+
storage URL. The Postdom bearer credential is never attached to that PUT, the PUT is never
|
|
78
|
+
retried, and the signed URL is not returned from the high-level result.
|
|
79
|
+
|
|
80
|
+
## Errors
|
|
81
|
+
|
|
82
|
+
Non-2xx responses raise typed subclasses of `PostdomAPIError`, including
|
|
83
|
+
`PostdomAuthenticationError`, `PostdomScopeError`, `PostdomHumanRouteError`,
|
|
84
|
+
`PostdomBillingError`, `PostdomRateLimitError`, and `PostdomServerError`. Network and deadline
|
|
85
|
+
failures use `PostdomConnectionError` and `PostdomTimeoutError`.
|
|
86
|
+
|
|
87
|
+
## Release status
|
|
88
|
+
|
|
89
|
+
Version `0.1.0` is the initial package version. PyPI publishing is held until Dean explicitly
|
|
90
|
+
authorizes it; building and audit do not imply registry publication.
|
|
@@ -0,0 +1,68 @@
|
|
|
1
|
+
[build-system]
|
|
2
|
+
requires = ["hatchling==1.27.0"]
|
|
3
|
+
build-backend = "hatchling.build"
|
|
4
|
+
|
|
5
|
+
[project]
|
|
6
|
+
name = "postdom"
|
|
7
|
+
version = "0.1.0"
|
|
8
|
+
description = "Typed sync and async Python client for the Postdom short-form publishing API."
|
|
9
|
+
readme = "README.md"
|
|
10
|
+
requires-python = ">=3.10"
|
|
11
|
+
license = { text = "Proprietary" }
|
|
12
|
+
authors = [{ name = "Postdom", email = "support@postdom.com" }]
|
|
13
|
+
keywords = [
|
|
14
|
+
"social media api",
|
|
15
|
+
"tiktok api",
|
|
16
|
+
"instagram reels api",
|
|
17
|
+
"youtube shorts api",
|
|
18
|
+
"schedule posts api",
|
|
19
|
+
"ai agent",
|
|
20
|
+
"short-form video",
|
|
21
|
+
]
|
|
22
|
+
classifiers = [
|
|
23
|
+
"Development Status :: 3 - Alpha",
|
|
24
|
+
"Programming Language :: Python :: 3",
|
|
25
|
+
"Programming Language :: Python :: 3.10",
|
|
26
|
+
"Programming Language :: Python :: 3.11",
|
|
27
|
+
"Programming Language :: Python :: 3.12",
|
|
28
|
+
"Programming Language :: Python :: 3.13",
|
|
29
|
+
"Programming Language :: Python :: 3.14",
|
|
30
|
+
"Typing :: Typed",
|
|
31
|
+
]
|
|
32
|
+
dependencies = [
|
|
33
|
+
"httpx>=0.27,<1",
|
|
34
|
+
"pydantic>=2.7,<3",
|
|
35
|
+
]
|
|
36
|
+
|
|
37
|
+
[project.urls]
|
|
38
|
+
Homepage = "https://postdom.com"
|
|
39
|
+
Repository = "https://github.com/deanfankhauser/postdom"
|
|
40
|
+
Issues = "https://github.com/deanfankhauser/postdom/issues"
|
|
41
|
+
|
|
42
|
+
[tool.hatch.build.targets.wheel]
|
|
43
|
+
packages = ["src/postdom"]
|
|
44
|
+
|
|
45
|
+
[tool.pytest.ini_options]
|
|
46
|
+
addopts = "-q"
|
|
47
|
+
testpaths = ["tests"]
|
|
48
|
+
|
|
49
|
+
[tool.ruff]
|
|
50
|
+
line-length = 100
|
|
51
|
+
target-version = "py310"
|
|
52
|
+
|
|
53
|
+
[tool.ruff.lint]
|
|
54
|
+
select = ["E", "F", "I", "UP", "B", "SIM"]
|
|
55
|
+
|
|
56
|
+
[tool.mypy]
|
|
57
|
+
python_version = "3.10"
|
|
58
|
+
strict = true
|
|
59
|
+
plugins = ["pydantic.mypy"]
|
|
60
|
+
packages = ["postdom"]
|
|
61
|
+
|
|
62
|
+
[dependency-groups]
|
|
63
|
+
dev = [
|
|
64
|
+
"build>=1.2,<2",
|
|
65
|
+
"mypy>=1.15,<2",
|
|
66
|
+
"pytest>=8.3,<9",
|
|
67
|
+
"ruff>=0.11,<1",
|
|
68
|
+
]
|
|
@@ -0,0 +1,138 @@
|
|
|
1
|
+
"""Postdom Python SDK."""
|
|
2
|
+
|
|
3
|
+
from ._client import AsyncPostdom, Postdom
|
|
4
|
+
from ._contract import (
|
|
5
|
+
DEFAULT_BASE_URL,
|
|
6
|
+
MEDIA_UPLOAD_STATUSES,
|
|
7
|
+
METRIC_AVAILABILITY_STATES,
|
|
8
|
+
OPERATIONS,
|
|
9
|
+
PERFORMANCE_METRICS,
|
|
10
|
+
PERFORMANCE_WINDOWS,
|
|
11
|
+
PLATFORM_LIMITS,
|
|
12
|
+
POLICY_VISIBILITY_OPTIONS,
|
|
13
|
+
POST_STATUSES,
|
|
14
|
+
POSTDOM_PLATFORMS,
|
|
15
|
+
PUBLISH_STATUSES,
|
|
16
|
+
SDK_VERSION,
|
|
17
|
+
SETTLED_POST_STATUSES,
|
|
18
|
+
TARGET_SETTINGS_KEYS,
|
|
19
|
+
TERMINAL_POST_STATUSES,
|
|
20
|
+
MediaUploadStatus,
|
|
21
|
+
MetricAvailability,
|
|
22
|
+
PerformanceMetric,
|
|
23
|
+
PerformanceWindow,
|
|
24
|
+
Platform,
|
|
25
|
+
PostStatus,
|
|
26
|
+
PublishStatus,
|
|
27
|
+
)
|
|
28
|
+
from ._errors import (
|
|
29
|
+
PostdomAPIError,
|
|
30
|
+
PostdomAuthenticationError,
|
|
31
|
+
PostdomBillingError,
|
|
32
|
+
PostdomConflictError,
|
|
33
|
+
PostdomConnectionError,
|
|
34
|
+
PostdomError,
|
|
35
|
+
PostdomHumanRouteError,
|
|
36
|
+
PostdomLockedError,
|
|
37
|
+
PostdomNotFoundError,
|
|
38
|
+
PostdomPermissionError,
|
|
39
|
+
PostdomRateLimitError,
|
|
40
|
+
PostdomScopeError,
|
|
41
|
+
PostdomServerError,
|
|
42
|
+
PostdomTimeoutError,
|
|
43
|
+
PostdomValidationError,
|
|
44
|
+
api_error_from_response,
|
|
45
|
+
parse_retry_after_seconds,
|
|
46
|
+
)
|
|
47
|
+
from ._models import (
|
|
48
|
+
AccountPerformanceResponse,
|
|
49
|
+
AccountPolicy,
|
|
50
|
+
BestPostsResponse,
|
|
51
|
+
BillingStatus,
|
|
52
|
+
ConnectAccountResponse,
|
|
53
|
+
LearningDigest,
|
|
54
|
+
MediaStatusResponse,
|
|
55
|
+
MediaUploadContract,
|
|
56
|
+
MetricAvailabilityDetail,
|
|
57
|
+
Plan,
|
|
58
|
+
Post,
|
|
59
|
+
PostdomAccount,
|
|
60
|
+
PostdomOptions,
|
|
61
|
+
PostEligibility,
|
|
62
|
+
PostPerformanceResponse,
|
|
63
|
+
PostPublish,
|
|
64
|
+
PostTarget,
|
|
65
|
+
PublishSubmission,
|
|
66
|
+
UploadMediaResult,
|
|
67
|
+
WorkspaceBrief,
|
|
68
|
+
WorkspaceStatus,
|
|
69
|
+
WorkspaceStatusAccount,
|
|
70
|
+
)
|
|
71
|
+
|
|
72
|
+
__version__ = SDK_VERSION
|
|
73
|
+
|
|
74
|
+
__all__ = [
|
|
75
|
+
"DEFAULT_BASE_URL",
|
|
76
|
+
"METRIC_AVAILABILITY_STATES",
|
|
77
|
+
"MEDIA_UPLOAD_STATUSES",
|
|
78
|
+
"OPERATIONS",
|
|
79
|
+
"PERFORMANCE_METRICS",
|
|
80
|
+
"PERFORMANCE_WINDOWS",
|
|
81
|
+
"PLATFORM_LIMITS",
|
|
82
|
+
"POLICY_VISIBILITY_OPTIONS",
|
|
83
|
+
"POST_STATUSES",
|
|
84
|
+
"POSTDOM_PLATFORMS",
|
|
85
|
+
"PUBLISH_STATUSES",
|
|
86
|
+
"SDK_VERSION",
|
|
87
|
+
"SETTLED_POST_STATUSES",
|
|
88
|
+
"TARGET_SETTINGS_KEYS",
|
|
89
|
+
"TERMINAL_POST_STATUSES",
|
|
90
|
+
"AccountPerformanceResponse",
|
|
91
|
+
"AccountPolicy",
|
|
92
|
+
"AsyncPostdom",
|
|
93
|
+
"BestPostsResponse",
|
|
94
|
+
"BillingStatus",
|
|
95
|
+
"ConnectAccountResponse",
|
|
96
|
+
"LearningDigest",
|
|
97
|
+
"MediaUploadStatus",
|
|
98
|
+
"MediaStatusResponse",
|
|
99
|
+
"MediaUploadContract",
|
|
100
|
+
"MetricAvailability",
|
|
101
|
+
"MetricAvailabilityDetail",
|
|
102
|
+
"PerformanceMetric",
|
|
103
|
+
"PerformanceWindow",
|
|
104
|
+
"Plan",
|
|
105
|
+
"Platform",
|
|
106
|
+
"Post",
|
|
107
|
+
"PostEligibility",
|
|
108
|
+
"PostPerformanceResponse",
|
|
109
|
+
"PostPublish",
|
|
110
|
+
"PostStatus",
|
|
111
|
+
"PostTarget",
|
|
112
|
+
"Postdom",
|
|
113
|
+
"PostdomAPIError",
|
|
114
|
+
"PostdomAccount",
|
|
115
|
+
"PostdomAuthenticationError",
|
|
116
|
+
"PostdomBillingError",
|
|
117
|
+
"PostdomConflictError",
|
|
118
|
+
"PostdomConnectionError",
|
|
119
|
+
"PostdomError",
|
|
120
|
+
"PostdomHumanRouteError",
|
|
121
|
+
"PostdomLockedError",
|
|
122
|
+
"PostdomNotFoundError",
|
|
123
|
+
"PostdomOptions",
|
|
124
|
+
"PostdomPermissionError",
|
|
125
|
+
"PostdomRateLimitError",
|
|
126
|
+
"PostdomScopeError",
|
|
127
|
+
"PostdomServerError",
|
|
128
|
+
"PostdomTimeoutError",
|
|
129
|
+
"PostdomValidationError",
|
|
130
|
+
"PublishSubmission",
|
|
131
|
+
"PublishStatus",
|
|
132
|
+
"UploadMediaResult",
|
|
133
|
+
"WorkspaceBrief",
|
|
134
|
+
"WorkspaceStatus",
|
|
135
|
+
"WorkspaceStatusAccount",
|
|
136
|
+
"api_error_from_response",
|
|
137
|
+
"parse_retry_after_seconds",
|
|
138
|
+
]
|