Getting started
Herald is a multi-tenant social-publishing API. Connect your accounts once, then publish to any of them from a single authenticated request.
Base URL
https://api.herald.leiritech.com
Every endpoint is under /v1. JSON in, JSON out, UTC ISO-8601 timestamps.
Authentication
Create an API key in the dashboard (Developer → API keys) and send it as a bearer token on every request:
curl https://api.herald.leiritech.com/v1/me \
-H "Authorization: Bearer hld_xxxxxxxxxxxxxxxxxxxxxxxx"
{ "tenant_id": "…", "tenant_name": "Acme" }
The key resolves to exactly one tenant; every response is scoped to that tenant —
there is no cross-tenant access. 401 unauthorized if the key is missing, invalid, or
revoked.
The publishing model in 30 seconds
- Connect an account → it gets a Herald UUID (
GET /v1/accounts). - Upload media (optional) →
POST /v1/mediareturns a media id. - Publish →
POST /v1/postswithtargets(account UUIDs),text, optionalmedia_ids, and amode(now/schedule/draft). - Track → poll
GET /v1/posts/{id}(per-target status + permalink) or subscribe a webhook and getpost.published/post.failedpushed to you.
One post can fan out to several networks at once; each target succeeds or fails independently (partial success is normal).
Errors
Failures return an appropriate 4xx/5xx and this shape:
{ "error": { "code": "validation_error", "message": "A video is required when publishing to YouTube or TikTok." } }
| HTTP | code | Meaning |
|---|---|---|
| 401 | unauthorized | missing/invalid/revoked API key |
| 404 | not_found | no such account/post for this tenant |
| 409 | conflict | wrong state (e.g. editing a published post) or network not configured |
| 422 | validation_error | bad body / unknown network / bad media / per-network rule |
Per-target publish failures never fail the whole request. Each failed target carries
one of: rate_limited, invalid_media, reauth_required, policy, exchange_failed,
unsupported.
Idempotency
Send external_ref on POST /v1/posts — a repeat with the same (tenant, external_ref)
returns the original post instead of publishing twice. Use your own internal id.
Pagination
List endpoints (GET /v1/accounts, GET /v1/posts) are cursor-paginated:
?limit— page size (default 25, max 100)?cursor— pass thenext_cursorfrom the previous response
Each response includes "next_cursor" (opaque string, or null on the last page).
Next
- API reference — every endpoint with examples.
- Networks — a page per network with real connect + publish examples and gotchas.
- SDKs — TypeScript and Python clients.