Metadata-Version: 2.4
Name: postdom
Version: 0.2.0
Summary: Typed sync and async Python client for the Postdom short-form publishing API.
Project-URL: Homepage, https://postdom.com
Project-URL: Repository, https://github.com/deanfankhauser/postdom
Project-URL: Issues, https://github.com/deanfankhauser/postdom/issues
Author-email: Postdom <support@postdom.com>
License: Proprietary
Keywords: ai agent,instagram reels api,schedule posts api,short-form video,social media api,tiktok api,youtube shorts api
Classifier: Development Status :: 3 - Alpha
Classifier: Programming Language :: Python :: 3
Classifier: Programming Language :: Python :: 3.10
Classifier: Programming Language :: Python :: 3.11
Classifier: Programming Language :: Python :: 3.12
Classifier: Programming Language :: Python :: 3.13
Classifier: Programming Language :: Python :: 3.14
Classifier: Typing :: Typed
Requires-Python: >=3.10
Requires-Dist: httpx<1,>=0.27
Requires-Dist: pydantic<3,>=2.7
Description-Content-Type: text/markdown

# postdom

Typed synchronous and asynchronous Python clients for the [Postdom](https://postdom.com)
short-form publishing API.

```bash
pip install postdom
```

```python
from pathlib import Path
import json
import os

from postdom import Postdom

# Supply metadata measured from this exact video, in a caller-created JSON file.
metadata = json.loads(Path("launch.metadata.json").read_text())
with Postdom(os.environ["POSTDOM_API_KEY"]) as postdom:
    upload = postdom.upload_media(
        Path("launch.mp4").read_bytes(),
        content_type="video/mp4",
        platforms=["tiktok"],
        width_pixels=metadata["width_pixels"],
        height_pixels=metadata["height_pixels"],
        duration_seconds=metadata["duration_seconds"],
    )
    media = postdom.wait_for_media(upload.media_handle)
    if media.status != "stored":
        raise RuntimeError(f"Media is {media.status}")

    submission = postdom.publish_video(
        account_ids=["account-tiktok"],
        media_handle=media.media_handle,
        caption="Launch day. Here is what we shipped.",
        intent="Announce the launch",
    )
    result = postdom.wait_for_publish(submission.id)
    print(result.status)
```

The async client has the same Python operation names:

```python
import os

from postdom import AsyncPostdom


async def workspace_status():
    async with AsyncPostdom(os.environ["POSTDOM_API_KEY"]) as postdom:
        return await postdom.get_workspace_status()
```

## Runnable examples and Postman

The repository's [`../examples`](../examples) directory contains a complete Python and Node
upload, publish, poll, and performance flow. [`../postman`](../postman) contains an importable
collection for all 16 agent-authority operations plus the credential-free signed media PUT.

Both SDK suites execute the examples against deterministic mock transports. They do not publish
content or contact Postdom, object storage, or a social platform.

## Contract

The package mirrors the 16 agent-authority operations in `@postdom/sdk`:

- `get_workspace_status`, `list_accounts`, `connect_account`
- `create_media_upload`, `get_media_status`, `upload_media`, `wait_for_media`
- `submit_plan`, `get_plan`
- `list_posts`, `list_post_events`
- `publish_video`, `get_post`, `wait_for_publish`
- `get_post_performance`, `get_account_performance`, `get_best_posts`
- `get_brief`, `get_digest`

Responses are Pydantic v2 models. Models allow additive server fields so a harmless API addition
does not break existing applications. The checked-in contract suite locks operation paths,
platforms, statuses, metrics, limits, and media handles to the audited Node SDK, which is itself
drift-tested against the live API route table and `@postdom/core`.

`list_posts` returns public summaries without internal media, approval, provider, or detailed
error fields. `list_post_events` returns durable publish-outcome and performance events for
incremental polling. Both support opaque cursor pagination; cursors must be reused with the same
filters that created them.

## Authentication and safety

Use a workspace `pd_live_` API key or `pd_oauth_` OAuth access token. Both carry agent authority:
approval, billing, and dashboard administration remain human-only and return
`403 agent_token_forbidden` by design. Credentials are held as Pydantic `SecretStr` values and
never appear in client representations or SDK-generated errors.

`publish_video` defaults TikTok to `SELF_ONLY`, Instagram to a Reel with AI disclosure, and YouTube
to private with synthetic-media disclosure. It generates one idempotency key and reuses it across
safe retries. `connect_account` is never retried because it has no idempotency key.

`upload_media` sends bytes through a separate HTTP client directly to the short-lived signed
storage URL. The Postdom bearer credential is never attached to that PUT, the PUT is never
retried, and the signed URL is not returned from the high-level result.

Both `create_media_upload` and `upload_media`, including their async equivalents, require
`width_pixels`, `height_pixels`, and `duration_seconds` as positive integers describing the
actual supplied video. Booleans, floating-point values, zero, negatives, and missing metadata
are rejected before requesting an upload. Measure the file before calling; the SDK does not
inspect video, infer defaults, or round metadata. Platform-specific limits remain enforced by
the API's current core contract. `create_media_upload` also requires the actual `size_bytes`;
`upload_media` derives that byte count from `data` and forwards your metadata unchanged.

## Errors

Non-2xx responses raise typed subclasses of `PostdomAPIError`, including
`PostdomAuthenticationError`, `PostdomScopeError`, `PostdomHumanRouteError`,
`PostdomBillingError`, `PostdomRateLimitError`, and `PostdomServerError`. Network and deadline
failures use `PostdomConnectionError` and `PostdomTimeoutError`.

## Release status

Version `0.2.0` adds the agent post and durable post-event feeds and repairs uploads for the
current API by requiring and forwarding measured video metadata. PyPI's `0.1.0` upload path is
not compatible with the current API. See the
[`CHANGELOG.md`](https://github.com/deanfankhauser/postdom/blob/main/packages/sdk/python/CHANGELOG.md)
for upgrade details.
Registry publication remains a separate, explicitly authorized step after audit and merge.
