Skip to main content

Documentation Index

Fetch the complete documentation index at: https://documentation.outpost.pub/llms.txt

Use this file to discover all available pages before exploring further.

When an event is published to Ghost, Outpost injects structured data into the post’s code injection footer. Your Ghost theme can read this data to render event details, registration CTAs, countdowns, and more.

Code Injection JSON

Outpost adds a <script> tag with JSON data to every event post:
<script id="event" type="application/json">
{
  "date": "March 15, 2026",
  "event_day": "Sunday",
  "start_time": "10:00 am",
  "end_time": "11:00 am",
  "time_zone": "America/New_York",
  "time_zone_abbr": "EDT",
  "location": "Online",
  "platform_url": "https://zoom.us/j/123456789",
  "registration_deadline": "March 14, 2026",
  "access": "anyone",
  "event_state": "registration_open",
  "outpost_ghost_event_id": "march-2026-webinar",
  "event_type": "webinar",
  "custom_fields": {
    "speaker_name": "Jane Smith",
    "session_type": "Workshop"
  }
}
</script>

Reading Data in Your Theme

JavaScript

document.addEventListener('DOMContentLoaded', function() {
  const eventScript = document.getElementById('event');
  if (!eventScript) return;

  try {
    const eventData = JSON.parse(eventScript.textContent);

    // Access standard fields
    console.log(eventData.date);           // "March 15, 2026"
    console.log(eventData.event_state);    // "registration_open"
    console.log(eventData.access);         // "anyone"

    // Access custom fields
    if (eventData.custom_fields) {
      console.log(eventData.custom_fields.speaker_name);  // "Jane Smith"
      console.log(eventData.custom_fields.session_type);  // "Workshop"
    }
  } catch (e) {
    console.error('Failed to parse event data', e);
  }
});

Handlebars (with inline script)

Since Handlebars templates run server-side and the JSON is in code injection (rendered client-side), you’ll typically use JavaScript to read the data and update the DOM:
{{!-- In your post.hbs or custom template --}}
<div class="event-details">
  <div class="event-date" data-event-field="date"></div>
  <div class="event-time" data-event-field="start_time"></div>
  <div class="event-location" data-event-field="location"></div>
  <div class="event-speaker" data-event-field="custom_fields.speaker_name"></div>
</div>

<script>
(function() {
  const eventScript = document.getElementById('event');
  if (!eventScript) return;

  const data = JSON.parse(eventScript.textContent);

  document.querySelectorAll('[data-event-field]').forEach(el => {
    const field = el.dataset.eventField;
    const value = field.split('.').reduce((obj, key) => obj?.[key], data);
    if (value) el.textContent = value;
  });
})();
</script>

Standard Field Reference

FieldTypeExampleDescription
datestring"March 15, 2026"Formatted event date (uses blog’s date format)
event_daystring"Sunday"Day of the week
start_timestring"10:00 am"Event start time
end_timestring"11:00 am"Event end time
time_zonestring"America/New_York"IANA timezone identifier
time_zone_abbrstring"EDT"Localized timezone abbreviation for the event date
locationstring"Online"Event location/venue
platform_urlstring"https://zoom.us/..."Virtual event platform link
registration_deadlinestring"March 14, 2026"Registration cutoff date (or event date if not set)
accessstring"anyone"Access level enum (see below)
event_statestring"registration_open"Current lifecycle state (see below)
outpost_ghost_event_idstring"march-2026-webinar"Unique event identifier (GUID/slug)
event_typestring"webinar"Event type key from manifest
custom_fieldsobject{...}Custom field values defined by manifest

Access Levels

The access field indicates who can register for the event:
ValueDescriptionTheme Behavior
anyoneAll logged-in members (free and paid)Show standard registration CTA
freeFree-tier members and aboveShow registration CTA for logged-in users
paidOnly paid subscribersShow upgrade CTA for free members

Example: Conditional Rendering by Access

const eventData = JSON.parse(document.getElementById('event').textContent);

if (eventData.access === 'paid') {
  // Show "Paid subscribers only" badge
  document.querySelector('.event-access-badge').textContent = 'Paid Subscribers Only';
  document.querySelector('.event-access-badge').classList.add('premium');
}

Event States

The event_state field reflects the event’s current lifecycle position:
ValueDescriptionTypical Theme Behavior
registration_openEvent is upcoming, registration is openShow registration CTA
registration_closedDeadline passed, event hasn’t happenedShow “Registration closed” message
event_passedEvent date/time has passedShow “This event has ended” message

Example: State-Based UI

const eventData = JSON.parse(document.getElementById('event').textContent);
const ctaContainer = document.querySelector('.event-cta');

switch (eventData.event_state) {
  case 'registration_open':
    ctaContainer.innerHTML = '<button class="register-btn">Register Now</button>';
    break;
  case 'registration_closed':
    ctaContainer.innerHTML = '<p class="closed">Registration is closed</p>';
    break;
  case 'event_passed':
    ctaContainer.innerHTML = '<p class="past">This event has ended</p>';
    break;
}

Custom Fields

Custom fields defined in the manifest are nested under the custom_fields object. The keys match what you defined in your outpost-manifest.yaml:
# In your manifest
custom_fields:
  - key: speaker_name
    type: text
  - key: session_type
    type: select
    options: [Workshop, Keynote, Panel]
// In your theme JavaScript
const eventData = JSON.parse(document.getElementById('event').textContent);

if (eventData.custom_fields?.speaker_name) {
  document.querySelector('.speaker-name').textContent =
    eventData.custom_fields.speaker_name;
}

if (eventData.custom_fields?.session_type) {
  document.querySelector('.session-type').textContent =
    eventData.custom_fields.session_type;
}

Custom Field Value Types

Manifest TypeJSON TypeExample Value
textstring"Jane Smith"
textareastring"A longer description..."
numbernumber60
selectstring"Workshop"
urlstring"https://example.com"
datestring"2026-03-15" (ISO format)
togglebooleantrue
imagestring"https://cdn.example.com/image.jpg"

Event Defaults Partial

Outpost also injects messaging defaults and portal URLs via the outpost-event-defaults.hbs partial during theme processing:
<script id="outpost-event-defaults" type="application/json">
{
  "messaging": {
    "can_register": {
      "cta_text": "Join this event",
      "button_text": "Register"
    },
    "registered": {
      "cta_text": "You're registered!",
      "button_text": "Add to Calendar"
    },
    ...
  },
  "urls": {
    "signin": "#/portal/signin",
    "signup": "#/portal/signup",
    "upgrade": "#/portal/account"
  }
}
</script>
This provides:
  • Messaging: Default CTA text and button labels for each audience state
  • URLs: Ghost portal links for sign-in, sign-up, and account/upgrade

Reading Event Defaults

const defaultsScript = document.getElementById('outpost-event-defaults');
if (defaultsScript) {
  const defaults = JSON.parse(defaultsScript.textContent);

  // Access messaging for current state
  const messaging = defaults.messaging?.can_register;
  if (messaging) {
    document.querySelector('.cta-text').textContent = messaging.cta_text;
    document.querySelector('.cta-button').textContent = messaging.button_text;
  }

  // Access portal URLs
  const signInUrl = defaults.urls?.signin || '#/portal/signin';
}

Complete Theme Example

Here’s a complete example of rendering event data in a custom Ghost template:
{{!-- custom-webinar.hbs --}}
{{!< default}}

<article class="event-post">
  <header class="event-header">
    <h1>{{title}}</h1>
    {{#if feature_image}}
      <img src="{{feature_image}}" alt="{{title}}" class="event-image" />
    {{/if}}
  </header>

  <div class="event-meta" id="event-meta-container">
    <!-- Populated by JavaScript from code injection -->
  </div>

  <div class="event-content">
    {{content}}
  </div>

  <div class="event-registration">
    <!-- Outpost widget renders here -->
    <div class="outpost-ghost-event-container"></div>
  </div>
</article>

<script>
document.addEventListener('DOMContentLoaded', function() {
  const eventScript = document.getElementById('event');
  if (!eventScript) return;

  const event = JSON.parse(eventScript.textContent);
  const container = document.getElementById('event-meta-container');

  container.innerHTML = `
    <div class="event-datetime">
      <span class="event-date">${event.date}</span>
      <span class="event-time">${event.start_time} - ${event.end_time}</span>
      <span class="event-timezone">${event.time_zone}</span>
    </div>
    <div class="event-location">${event.location}</div>
    ${event.custom_fields?.speaker_name ? `
      <div class="event-speaker">
        <strong>Speaker:</strong> ${event.custom_fields.speaker_name}
      </div>
    ` : ''}
    ${event.event_state === 'event_passed' ? `
      <div class="event-status past">This event has ended</div>
    ` : ''}
  `;
});
</script>

Ghost Post Properties

In addition to code injection, Outpost sets these Ghost post properties:
PropertyValue
titleEvent name
custom_excerptEvent description
feature_imageEvent featured image (if uploaded)
custom_templateTemplate from manifest post_template
tagsMerged from default_tags + event type tags
These are accessible via standard Ghost Handlebars helpers:
<h1>{{title}}</h1>
<p class="excerpt">{{excerpt}}</p>
{{#if feature_image}}
  <img src="{{feature_image}}" />
{{/if}}

API Endpoint for Registration

The event widget uses Outpost’s plugin API for registration. If you’re building a custom registration UI:
// Register a member for an event
const response = await fetch(
  `/plugin-api/ghost-event/${apiKey}/${eventGuid}/subscribe/${memberUid}`,
  { method: 'POST' }
);

// Cancel registration
const response = await fetch(
  `/plugin-api/ghost-event/${apiKey}/${eventGuid}/unsubscribe/${memberUid}`,
  { method: 'POST' }
);

// Get calendar file
window.location.href = `/plugin-api/ghost-event/${apiKey}/${eventGuid}/calendar.ics`;
The apiKey is your blog’s Outpost GUID (available in the outpost-api-key.hbs partial). The memberUid is the logged-in member’s Ghost UUID.