Developer API
Customer site integration API
Your site pulls the posts Trina has ready, publishes them itself, and reports the live URL back. Trina then checks the live page, the same way it checks any post you publish.
How it works
- Issue a key in the console: Settings → Channel setup → Customer site integration API. Only the brand owner can issue or revoke keys.
- Your server calls GET /posts?status=ready and then GET /posts/{id} for the Markdown and HTML body.
- Publish the post on your site.
- Call POST /posts/{id}/published with the live URL. The post moves to published and Trina starts checking it.
Authentication
Send the key as a Bearer token on every request. A key belongs to one brand and only ever sees that brand's posts.
Authorization: Bearer trn_live_…- The full key is shown once, when it is issued. Trina stores only a hash, so a lost key cannot be recovered - revoke it and issue a new one.
- Keep the key on your server (for example in an environment variable). Never put it in browser code or a public repository.
- A revoked key stops working immediately (401). A brand can have up to 5 active keys. The same limits apply on every plan.
Endpoints
Base URL: https://trina.app/api/v1
List postsGET /posts
Newest first, without the body. Query parameters (all optional):
statusready (default) or publishedsinceISO 8601 date-time. ready: created at or after it. published: published at or after it.channelblog (default) - your own sitelimit1–100, default 50curl -s "https://trina.app/api/v1/posts?status=ready&since=2026-10-01T00:00:00Z" \
-H "Authorization: Bearer $TRINA_API_KEY"{
"posts": [
{
"id": "d-2026-10-16-blog-1",
"title": "치아 미백, 집에서 해도 될까",
"status": "ready",
"channel": "blog",
"excerpt": "치아 미백을 집에서 할 때 확인할 것들을 정리했습니다…",
"cover_image_url": "https://…/cover.png",
"seo_score": 82,
"scheduled_for": null,
"created_at": "2026-10-16T01:00:00.000Z",
"published_at": null,
"post_url": null,
"verify_state": null
}
]
}Get one postGET /posts/{id}
Returns the body as markdown and as html. The HTML carries inline styles so it reads well without extra CSS; raw HTML inside a draft is dropped, never passed through.
curl -s "https://trina.app/api/v1/posts/d-2026-10-16-blog-1" \
-H "Authorization: Bearer $TRINA_API_KEY"{
"post": {
"id": "d-2026-10-16-blog-1",
"title": "치아 미백, 집에서 해도 될까",
"status": "ready",
"markdown": "## 집에서 하는 미백…",
"html": "<h2 style=\"…\">집에서 하는 미백…</h2>…",
"canonical_url": null,
"verify_note": null,
"verify_at": null,
"…": "plus every field from the list"
}
}Report the published URLPOST /posts/{id}/published
Send the full https:// URL of the live page. The post is marked published (post_url, published_at) and verify_state becomes checking; Trina then checks the page and sets it to confirmed or mismatch. Sending a new URL later replaces the old one.
curl -s -X POST "https://trina.app/api/v1/posts/d-2026-10-16-blog-1/published" \
-H "Authorization: Bearer $TRINA_API_KEY" \
-H "Content-Type: application/json" \
-d '{"url":"https://www.example.com/blog/teeth-whitening-at-home"}'{
"post": {
"id": "d-2026-10-16-blog-1",
"status": "published",
"published_at": "2026-10-16T05:12:44.120Z",
"post_url": "https://www.example.com/blog/teeth-whitening-at-home",
"verify_state": "checking",
"…": "…"
}
}Post fields
idPost id, used in the paths abovetitleTitlestatusready or publishedexcerptPlain-text summary for a meta description (about 160 characters)cover_image_urlCover image URL, or nullscheduled_forPlanned publish date (YYYY-MM-DD), or null. Publish on or after it.post_urlThe URL you reported, or nullverify_statenull · checking · confirmed · mismatch · not_found · wrong_channelmarkdown / htmlBody (single-post response only)Errors
Errors are JSON: { "error": "code", "hint"?: "…" }.
400invalid_status · invalid_since · invalid_channel · invalid_limit · invalid_json · url_not_https401missing_api_key · invalid_api_key (unknown, malformed or revoked key)403forbidden - the post belongs to another brand404post_not_found422wrong_channel - the URL's host belongs to a different channel (e.g. a Naver link for a blog post)500internal_error - retry laterHTTP/1.1 403
{ "error": "forbidden", "hint": "This post belongs to another brand." }Coming next
Push delivery (a webhook to your site when a post is ready) is planned. Until then, poll GET /posts?status=ready on a schedule - every 15–60 minutes is plenty.