Introduction
Timepath Liveblog delivers real-time event coverage. In headless mode you render your own front-end from our API instead of using the Timepath embed — you fetch the structured content and build your own components around it.
This page is for two audiences: editors who author the Liveblog Introduction, and developers who consume the data and render their own components. It applies to every Timepath client — the schema and endpoint are the same for all organizations.
The Liveblog Introduction is the header block that appears above the update stream. It is composed of four movable sections:
- 1
Media header
Image, video, or embed shown at the top of the block.
- 2
Introduction
A short rich-text intro paragraph.
- 3
Summary title
The heading above the key points.
- 4
Key points
Short takeaways ("Kernpunten" / "Hoofdpunten" in Dutch), each of which can optionally link to a liveblog update or to an external article.
For editors: adding key points
- 1
Open the Introduction editor
Open the liveblog and click the Introduction editor (Cover Studio).
- 2
Open the Key points block
In the rail, open the Key points block.
- 3
Add a key point
Click Add key point and type the text. Wrapping text in **double asterisks** renders it bold.
- 4
Choose the link target
Use the toggle on the key point to pick where it links (see the two options below).
- 5
Reorder, remove, and save
Reorder or remove key points with the row controls. Save to publish.
The two link targets:
Update
Search and pick an existing liveblog update. Clicking the key point scrolls the reader to that update. If the linked update is later deleted, the editor shows an "Update deleted" warning and the key point falls back to plain text.
External link
Paste a URL to another article (e.g. https://…). Clicking the key point opens that URL in a new tab. Invalid URLs show a warning and are not linked.
(Screenshots: the Key points block, the Update picker, and the External-link input — add the ones from the walkthrough here.)
For developers: consuming the data
Everything below describes the headless data contract. Because you render your own components, the structured fields are the source of truth.
The data endpoint
metadata endpoint omits all cover / intro fields — only data returns the Introduction block and key points.GET https://embed.timepath.co/api/external/liveblog/data
?liveblogId={liveblogId}
&eventsToShow={n} # optional, default 25
&offset={n} # optional, default 0
Header: x-api-key: <your organization API key>Example request:
curl "https://embed.timepath.co/api/external/liveblog/data?liveblogId=abc123&eventsToShow=25&offset=0" \
-H "x-api-key: YOUR_ORGANIZATION_API_KEY"Query parameters:
| Parameter | Required | Default | Notes |
|---|---|---|---|
liveblogId | Yes | — | The liveblog to fetch. |
eventsToShow | No | 25 | Number of updates to return per page. |
offset | No | 0 | Number of updates to skip (pagination). |
Response shape
A single response returns the liveblog object (the Introduction block plus styling), the paginated liveblogevents array (the update stream), and pagination metadata.
{
"liveblog": {
"id": "abc123",
// ── INTRODUCTION ──────────────────────────────
"cover_media": { // header media, nullable
"type": "image", // "image" | "video" | "embed"
"url": "https://…",
"caption": "…",
"credits": "…"
},
"cover_intro_body": "<p>…</p>", // introduction, rich-text HTML
"cover_summary_title": "…", // heading shown above the key points
// ── KEY POINTS ────────────────────────────────
"cover_summary_items": [
{
"id": "cuid…",
"text": "**bold** supported markdown",
"link_type": "update", // "update" | "external" ← authoritative
"event_id": "evt789", // set when link_type = "update"; else null
"event_title": "…", // ⚠ internal display cache — do NOT rely on it
"external_url": null // set (string) when link_type = "external"
}
],
// ── LAYOUT & STYLING (optional to consume) ────
"cover_section_order": ["media","intro","summary_title","key_points"],
"cover_section_order_v": 2, // honour cover_section_order only when >= 2
"color_header_keypoints": "#000000", // text / link colour
"color_header_keypoints_background": "#eef2ff", // box background
"header_keypoints_background_opacity": 100, // 0–100
"header_keypoints_padding": 20, // px
"header_keypoints_radius": 14, // px
// ── CONVENIENCE (optional) ────────────────────
"cover_body": "<…pre-rendered HTML of the whole intro…>"
},
"liveblogevents": [
{
"id": "evt789", // matches cover_summary_items[].event_id
"title": "…",
"date": { "seconds": 1720345678 }, // Firestore timestamp
"created": { "seconds": 1720345600 },
"body": "<…>", "pinned": false
}
],
"totalEvents": 42, "offset": 0, "limit": 25, "hasMore": true
}Resolving a key point's link
link_type is authoritative. For each item in cover_summary_items, resolve the link like this:
if link_type == "external" and external_url is a valid http(s) URL
→ external link (open in a new tab: target="_blank" rel="noopener noreferrer")
else if event_id resolves to an entry in liveblogevents
→ internal link to that update (see "Internal links" below)
else → plain text (no link)link_type field. Treat a missing or unrecognized link_type as "update".Internal links (link to an update)
A key point's internal link is an in-page anchor, not an absolute URL. Timepath's own reader renders href="#liveblog-event-{event_id}" and gives each update a matching DOM id id="liveblog-event-{event_id}".
Because you render your own components, you must do one of:
- Reproduce the anchor scheme — render each update with
id="liveblog-event-{event_id}"and link the key point to#liveblog-event-{event_id}(works when the full update list is on the same page), or - Map
event_idto your own canonical article/update URL.
<!-- 1. Link the key point to the update's in-page anchor -->
<a href="#liveblog-event-evt789">{key point text}</a>
<!-- 2. Give the rendered update a matching DOM id -->
<div id="liveblog-event-evt789">
<!-- your update markup -->
</div>Relative time per key point ("2 hours ago")
Relative time is not stored on the item. Resolve event_id against liveblogevents, take the linked event's date.seconds (fallback created.seconds), and compute the relative time yourself.
External-linked key points have no associated time.
Fields to treat as internal (do not depend on)
event_titleA denormalized display cache. It can go stale (it is not cleaned when an update is deleted). Resolve the real title from
liveblogeventsusingevent_id.cover_bodyA pre-rendered HTML snapshot, baked when the editor saves. Convenient, but it can lag the structured fields. For custom rendering prefer the structured fields (
cover_intro_body,cover_summary_items).Key-points box heading label
The box label ("Key points" / "Hoofdpunten") is derived from the liveblog's language, not a payload field.
cover_summary_titleis a separate, editor-set heading. Supply and localize the box label yourself.
Field reference — a key point
Each entry in cover_summary_items[]:
| Field | Type | Notes |
|---|---|---|
id | string | Stable client id. |
text | string | **bold** markdown supported. |
link_type | "update" | "external" | Authoritative. Absent on legacy items ⇒ treat as "update". |
event_id | string | null | Linked update id (when link_type = update). Resolve against liveblogevents. |
event_title | string | null | Internal display cache — do not rely on it. |
external_url | string | null | External article URL (when link_type = external). |
FAQ
Questions or need an absolute update URL / a pinned, versioned payload schema? Visit the support centre or contact us.