# postcache

> Public, short-lived media URLs for AI agents that post to Instagram, Threads and Facebook. Upload a file with an API key, get an HTTPS URL that passes the platform's media checks (or have postcache fix the file), delete it after the post.

Status: early access; REST API and MCP are live for early-access keys (hello@postcache.dev). Docs: https://postcache.dev/docs · OpenAPI 3.1: https://postcache.dev/openapi.json

## API

Base URL: https://postcache.dev. Auth: `Authorization: Bearer pc_live_...`.

- `PUT /v1/media` with the file as the raw body. Headers: `x-target` (one of instagram.feed, instagram.carousel, instagram.reels, instagram.story, threads, facebook.page), optional `x-filename`. Query: `ttl` seconds, 3600-604800, default 86400; `convert=crop|pad`; `dry_run=1`.
  - 201: `{ id, status: "ready", url, target, content_type, size, expires_at, checks }`. Give `url` to the platform API (image_url / video_url).
  - 202 (with convert, file fixable): `{ id, status: "converting", original_checks, status_url }`. Poll `GET /v1/media/{id}` until status is `ready` (url set) or `failed` (checks say why).
  - 422: `{ error: "checks_failed", checks: { problems: [{ rule, message, fix }] } }`. Not fixable by convert (e.g. duration): apply the `fix` and upload again.
  - 200 (dry_run): `{ dry_run: true, checks, convertible? }`, nothing stored.
  - 401 bad key, 400 bad target/ttl/convert, 402 `quota_exceeded` (plan limit; `upgrade_url`, `buy_url`) or `payment_required` (no key: pay per use), 413 over 1 GiB, 429 `budget_exceeded` (key's daily cap; Retry-After), 503 busy (Retry-After).
- `POST /v1/media/from-url` JSON `{ source_url, target, filename?, ttl?, convert?, dry_run? }`: same answers; only public http(s) URLs (400 `blocked` for private addresses).
- `GET /v1/media/{id}`: status (converting, ready, failed, deleted), url, expiry, fetch count, checks.
- `DELETE /v1/media/{id}`: delete now (204). Call it after the post is published. Without a key (pay per use) send `x-delete-token` from the upload answer; the same header reads the status.

## Pay per use (no account)

- Send `PUT /v1/media` or `POST /v1/media/from-url` without Authorization: 402 with an MPP challenge (`WWW-Authenticate`, USDC on Tempo) and an x402 challenge (`Payment-Required`, USDC on Base). Price: 0.01 USD per upload. Pay, then repeat the request.
- The paid answer has `payment` and `delete_token` (shown once). Payment is taken before the checks: test with `dry_run=1` first (free, no payment).
- `POST /v1/credits`: 500 uploads for 5 USD (card via MPP or USDC). No key: answer has a new API key with 500 credits. With a key: credits are added to that account.
- Media URLs: `https://cdn.postcache.dev/m/{id}/{name}`; 410 after expiry or delete; single byte ranges (416 past the end).

Plans: Free 100 uploads a month, Builder 2,000, Studio 20,000. Dry runs are free. Manage keys, caps and billing at https://postcache.dev/dashboard.

What convert fixes: images (format, aspect, colour space, size → sRGB JPEG; crop, or pad with white) and video (codecs, container, pixel format, interlacing, fps, width, aspect, bitrate, moov, edit lists, size → H.264/AAC MP4; crop, or pad with black). It never trims duration.

## MCP

`https://postcache.dev/mcp` (Streamable HTTP), header `Authorization: Bearer pc_live_...`. Server card: https://postcache.dev/mcp/server-card. Tools:
- `upload_media { target, source_url | data_base64 (max 10 MB), filename?, ttl?, convert? }`
- `check_media` (same input, stores nothing)
- `get_media { id }`, `delete_media { id }`

## Contact

hello@postcache.dev
