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.
@@ -0,0 +1,8 @@
1
+ .venv/
2
+ dist/
3
+ *.egg-info/
4
+ .pytest_cache/
5
+ .mypy_cache/
6
+ .ruff_cache/
7
+ __pycache__/
8
+ *.py[cod]
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.
@@ -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
+ ]