Reference

External Integration API

Embed a Cronolog timeline in your own site and let authenticated users create or edit events in a popup window — without leaving your page.

How it works

The integration has three moving parts:

  1. 1. API key Your server holds a secret API key tied to one Cronolog timeline. It uses this key to generate short-lived, one-time magic links on demand.
  2. 2. Magic link Each magic link signs the Cronolog user in automatically and opens a specific form — either "add an event" or "edit this event". The link expires after 15 minutes and can only be used once.
  3. 3. Popup window The form opens in a small popup window. When the user saves or deletes the event, the popup posts a message back to your page and closes itself. Your page can refresh the relevant content without a full reload.
Your page                    Your server                  Cronolog
────────                     ───────────                  ────────
User clicks "Edit"
  ──── POST /your-api ──────►
                             POST /api/auth/magic_link
                               (Bearer: YOUR_API_KEY)
                             ◄──── { url: magic_link } ──
  ◄── magic link URL ───────
window.open(magic_link)  ──────────────────────────────► /auth/magic?t=TOKEN
                                                         sign in user
                                                         redirect to edit form
                                                         user edits & saves
window receives postMessage ◄───────────────────────────── popup closes
refresh event on page

Prerequisites

  • A Cronolog account with at least one timeline.
  • An API key for that timeline — create one at Settings → API Keys.
  • A server that can make outbound HTTPS requests — the API key must never be exposed to a browser.

Step 1 — Create an API key

Go to Settings → API Keys and create a key. You will be asked for:

Label A human-readable name so you can tell keys apart (e.g. "Production site").
Timeline The specific timeline this key is authorised to add and edit events on.
Allowed return hosts One or more hostnames your site runs on (e.g. example.com, localhost). Magic links will only accept a return_to URL whose host is in this list. Enter one hostname per line — no scheme or path.

The raw token is shown once at creation time. Store it securely (environment variable, secrets manager). It cannot be recovered — only rotated.

Keep the API key on your server. Never include it in client-side JavaScript, HTML source, or public version control. Anyone who has it can generate magic links that sign users into your Cronolog account.

Step 2 — Request a magic link

When a user on your site wants to add or edit an event, your server makes a signed request to Cronolog.

Endpoint

POST https://cronolog.ca/api/auth/magic_link

Request headers

Authorization: Bearer YOUR_API_KEY
Content-Type: application/x-www-form-urlencoded

Request body parameters

return_to required The URL on your site that opened the popup. The popup posts a message to this origin after saving. Must be on the key's allowed return host.
event_id optional The Cronolog event ID to edit. Omit this parameter to open the "add a new event" form instead.

Success response 200 OK

{
  "url": "https://cronolog.ca/auth/magic?t=TOKEN"
}

Open this URL in a popup window. It is single-use and expires in 15 minutes.

Error responses

Status Body Cause
401 {"error":"Unauthorized"} Missing, invalid, or revoked API key
402 {"error":"Subscription required…"} Key owner's Cronolog subscription has lapsed
422 {"error":"return_to is required"} return_to param missing
422 {"error":"return_to host does not match…"} return_to is on a different host than the key allows
404 {"error":"Event not found"} event_id does not exist
403 {"error":"Event does not belong to this timeline"} event_id belongs to a different timeline
429 {"error":"Too many requests…"} More than 30 requests per minute per key

Example — curl

# New event
curl -X POST https://cronolog.ca/api/auth/magic_link \
  -H "Authorization: Bearer clk_YOUR_KEY_HERE" \
  --data-urlencode "return_to=https://example.com/timeline"

# Edit an existing event
curl -X POST https://cronolog.ca/api/auth/magic_link \
  -H "Authorization: Bearer clk_YOUR_KEY_HERE" \
  --data-urlencode "return_to=https://example.com/timeline" \
  --data-urlencode "event_id=42"

Step 3 — Open the popup

Pass the magic link URL to window.open(). The popup signs the user in and opens the event form. When the user saves, deletes, or closes the form, the popup posts a message to your page and closes itself.

Client-side JavaScript

// 1. Fetch a magic link from your own server (never expose the API key here).
async function openEditPopup(eventId) {
  const res  = await fetch('/your-server/magic-link', {
    method: 'POST',
    headers: { 'Content-Type': 'application/json' },
    body: JSON.stringify({ eventId })
  })
  const { url } = await res.json()

  // 2. Open the popup.
  const popup = window.open(
    url,
    'cronolog-edit',
    'width=780,height=700,resizable=yes,scrollbars=yes'
  )

  // 3. Listen for the result.
  window.addEventListener('message', function handler(event) {
    if (event.origin !== 'https://cronolog.ca') return
    const { type, eventId } = event.data

    if (type === 'cronolog:saved')  { /* event created or updated */ }
    if (type === 'cronolog:deleted') { /* event was deleted */ }

    window.removeEventListener('message', handler)
    popup?.close()
  })
}

// "Add event" — omit eventId
function openNewEventPopup() {
  return openEditPopup(null)
}

postMessage payload

When the popup closes after a successful save or delete, your page receives a window.message event with this shape:

{
  type:    "cronolog:saved"   // or "cronolog:deleted"
  eventId: 42                 // Cronolog event ID (0 for a newly created event)
}

The message origin is always https://cronolog.ca — always verify this before acting on the payload.

No message is sent if the user closes the popup without saving.

Do not open the popup with noopener — that severs window.opener and the message will never arrive.

For a newly created event, eventId is the Cronolog ID of the new record. You can store this to construct an edit link for later.

Surfacing event IDs

To build an "edit" button for each event on your page, you need the Cronolog event ID for each one. The timeline JSON feed — available at https://cronolog.ca/t/YOUR_TOKEN.json — includes an id field on every event.

{
  "timeline": { ... },
  "events": [
    {
      "id": 42,
      "title": "Moved to Vancouver",
      "date": "2019",
      ...
    }
  ]
}

Fetch this feed when rendering your page and pass each event's id to the magic link request when the user clicks its edit button. See the format reference for the full JSON schema.

Key management

All key management is done in Settings → API Keys.

Rotate Generates a new key and immediately revokes the old one. Update your server's environment variable without downtime by deploying the new key before rotating, or by treating a short window of 401s as acceptable.
Revoke Permanently deactivates a key. Any subsequent requests using that key will receive a 401. Use this if a key is compromised.

Security notes

  • The API key must only be used server-side. It is a long-lived credential with write access to your timeline.
  • Magic links are one-time and expire after 15 minutes. There is no harm in generating many of them — each is independent.
  • The return_to host is validated against the key's allowed return host. A magic link cannot be used to redirect back to an arbitrary site.
  • Always check event.origin === 'https://cronolog.ca' before acting on a postMessage. Do not trust messages from other origins.
  • The popup is opened on the cronolog.ca domain. Cronolog handles the authentication and form — your site never handles credentials.
  • Rate limiting is applied per API key: 30 requests per minute. This is more than sufficient for interactive use.

Complete example

A minimal server-side handler and the client-side JavaScript needed to wire up an edit button for each event on a page.

Server — generate a magic link

# Pseudo-code — adapt to your server framework

POST /your-server/magic-link
  body: { eventId: 42 }           # or null for new event

→ POST https://cronolog.ca/api/auth/magic_link
    Authorization: Bearer $CRONOLOG_API_KEY
    return_to: https://example.com/timeline-page
    event_id:  42                  # omit if creating new

← { "url": "https://cronolog.ca/auth/magic?t=TOKEN" }

→ return { url } to the browser

Client — open popup and handle result

const CRONOLOG_ORIGIN = 'https://cronolog.ca'

document.querySelectorAll('[data-edit-event]').forEach(btn => {
  btn.addEventListener('click', async () => {
    const eventId = btn.dataset.editEvent || null

    // 1. Ask your server for a magic link.
    const res = await fetch('/your-server/magic-link', {
      method: 'POST',
      headers: { 'Content-Type': 'application/json' },
      body: JSON.stringify({ eventId })
    })
    if (!res.ok) { console.error('Could not get magic link'); return }
    const { url } = await res.json()

    // 2. Open the popup.
    window.open(url, 'cronolog-edit', 'width=780,height=700,resizable=yes,scrollbars=yes')
  })
})

// 3. Listen for save/delete confirmation from the popup.
window.addEventListener('message', event => {
  if (event.origin !== CRONOLOG_ORIGIN) return

  const { type, eventId } = event.data

  if (type === 'cronolog:saved') {
    console.log('Event saved, Cronolog ID:', eventId)
    // e.g. refetch your timeline JSON and re-render
  }

  if (type === 'cronolog:deleted') {
    console.log('Event deleted, Cronolog ID:', eventId)
    // e.g. remove the event from the DOM
  }
})

HTML — edit buttons

<!-- Edit an existing event -->
<button data-edit-event="42">Edit event</button>

<!-- Add a new event (no ID needed) -->
<button data-edit-event="">Add event</button>