Program Planner DOCS & HELP

Public REST API

The public interface under convention-schedule-planner/public/v1 delivers the program read-only as JSON – for apps, partner websites, info screens and signage systems. Switching it on and issuing keys is covered in API tab; this page is about connecting to it.

A contract that holds

The API has its own fixed data format, independent of the calendar display. Fields may be added but are never renamed or removed. If a field is missing, the corresponding data scope is switched off in the API tab.

Address and key

All addresses start with the base address, e.g. https://your-site.com/wp-json/convention-schedule-planner/public/v1. With “Plain” permalinks it looks different (?rest_route=/convention-schedule-planner/public/v1/…) – the address shown in the API tab is always correct.

MethodExampleNote
Header X-EC-API-KeyX-EC-API-Key: ecpk_…Recommended.
Header AuthorizationAuthorization: Bearer ecpk_…Equivalent.
Parameter api_key?api_key=ecpk_…Only for tools without headers – the key then ends up in server logs.
No key in public JavaScript

Whatever runs in your visitors’ browsers can be read by anyone. For widgets on other websites, choose “Public” access or fetch the data via a server.

Endpoints

EndpointContent
GET /infoSite name, time zone, API version, data scope, counts, primary color. Readable without a key.
GET /eventsSessions, paginated, with filters (see below).
GET /events/{id}One session or main event including the description.
GET /venuesAll venues with hierarchy, color and image. hide_empty=true shows only those in use.
GET /categoriesAll categories with hierarchy and color.
GET /speakersPublic speaker profiles, paginated, with search. Only with the “Speakers” data scope enabled. The company_logo field, here and in /speakers/{id}, holds the address of the linked exhibitor’s featured image, otherwise null, regardless of the “Speaker: Company Logo” switch.
GET /speakers/{id}One speaker profile with description and the IDs of their sessions.
GET /openapiMachine-readable description (OpenAPI 3.1) for Postman, Insomnia, Swagger UI and code generators. Readable without a key.

Terms: a main event (fair, festival) contains sessions. Sessions without a main event count as sessions as well.

Parameters of /events

ParameterValues / defaultMeaning
typeitem · main · all – default itemSessions, main events or both.
parentIDOnly sessions of this main event.
from, toYYYY-MM-DDPeriod. Main events count if their period overlaps.
venueIDs or slugs, comma-separatedIncludes sub-venues – the filter for a hall also finds its rooms.
categoryIDs or slugs, comma-separatedIncludes sub-categories.
searchtextFull-text search in title, short description and description.
statusany · scheduled · cancelled – default anyFilter by status.
modified_sinceISO 8601Only entries changed since then – for incremental synchronisation.
orderby, orderstart · title · modified; asc · descDefault: by start, ascending.
speakerIDOnly sessions with this speaker (“Speakers” data scope).
includedescriptionLong description in lists too (“Description” data scope).
page, per_pagefrom 1; 1–100, default 50Pagination.
_fieldsfield listOnly the named fields, e.g. _fields=id,title,start. Works for every endpoint.

Filters can be combined freely with &. An unknown venue or category returns an empty list, not all entries.

Typical requests
/events?type=main
/events?parent=123&per_page=100
/events?parent=123&from=2026-10-17&to=2026-10-17&venue=main-hall
/events?parent=123&status=cancelled
/events?modified_since=2026-10-17T00:00:00Z
/events?parent=123&_fields=id,title,start,end,venue

What a session looks like

Response (shortened)
{
  "id": 4711,
  "type": "item",
  "status": "scheduled",
  "title": "Workshop: Painting fairy wings",
  "date": "2026-10-17",
  "start_time": "14:00",
  "end_time": "15:30",
  "start": "2026-10-17T14:00:00+02:00",
  "end": "2026-10-17T15:30:00+02:00",
  "parent": { "id": 123, "title": "Convention 2026" },
  "venue": { "id": 12, "slug": "workshop-room", "name": "Workshop Room" },
  "categories": [ { "id": 7, "slug": "workshop", "name": "Workshop" } ],
  "color": "#8e44ad",
  "image": { "url": "https://…/workshop.jpg", "is_fallback": false },
  "speakers": [ { "id": 55, "name": "Jane Doe", "role": "Speaker" } ],
  "price": "12 €",
  "ticket_url": "https://tickets.example.com/",
  "permalink": "https://…/events/workshop-painting-fairy-wings/",
  "url": "https://…/events/workshop-painting-fairy-wings/",
  "link_type": "page",
  "modified": "2026-10-17T09:12:00+00:00"
}
FieldMeaning
start_time, end_time / start, endLocal time – and additionally complete with date and time zone. If the end is before the start, the session runs past midnight; end is then on the following day.
typeitem or main. Main events additionally carry date_range and items_count.
statuscancelled for cancellations. Cancelled sessions stay in the list so apps can show the cancellation.
url, link_typeWhere a click leads – as on the website: detail page, external link or nowhere (null). permalink is always the detail page.
speakers[].idOnly set for public speaker profiles, otherwise null. The name is always included.
colorHighlight color of the venue or category. If null, use primary_color from /info.

Pages and freshness

Lists come in pages of at most 100 entries. The total is in the X-WP-Total and X-WP-TotalPages headers; the Link header contains the address of the next page.

  • ETag (recommended): Every response carries an ETag. If the application sends it back as If-None-Match, unchanged data returns a short 304 Not Modified.
  • modified_since: Returns only what changed. Deleted or unpublished sessions simply no longer appear – so reload the whole program regularly (e.g. once a day) and remove missing IDs.

Sensible intervals: every 5 to 15 minutes during the event, a few times a day otherwise. The rate limit (default 120 requests per minute) is far above that.

Code examples

JavaScript – load all pages of a program
const BASE = 'https://your-site.com/wp-json/convention-schedule-planner/public/v1';
const KEY  = 'ecpk_…';

async function loadProgram(parentId) {
  const events = [];
  for (let page = 1; ; page++) {
    const res = await fetch(BASE + '/events?parent=' + parentId + '&per_page=100&page=' + page, {
      headers: { 'X-EC-API-Key': KEY }
    });
    if (!res.ok) throw new Error('HTTP ' + res.status);
    events.push(...await res.json());
    if (page >= Number(res.headers.get('X-WP-TotalPages'))) break;
  }
  return events;
}
PHP – on a WordPress partner site, cached for 10 minutes
$events = get_transient( 'partner_program' );

if ( false === $events ) {
    $response = wp_remote_get(
        'https://your-site.com/wp-json/convention-schedule-planner/public/v1/events?parent=123&per_page=100',
        array( 'headers' => array( 'X-EC-API-Key' => 'ecpk_…' ), 'timeout' => 15 )
    );
    $events = ( 200 === wp_remote_retrieve_response_code( $response ) )
        ? json_decode( wp_remote_retrieve_body( $response ), true )
        : array();
    set_transient( 'partner_program', $events, 10 * MINUTE_IN_SECONDS );
}
Examples with your own data

The plugin help in the back end shows the same examples with a real main event and a real venue of your website – every address can be copied and tried out right there.

Error messages

Errors arrive as JSON with code, message and data.status – the same format as the WordPress REST API.

StatusCodeMeaning
400rest_invalid_paramA parameter has the wrong format, e.g. from=17.10.2026 instead of 2026-10-17.
400ec_api_invalid_pageThe requested page is beyond the last one.
401ec_api_key_missingNo key sent, but the API requires one.
401ec_api_key_invalidThe key is wrong or revoked.
404rest_no_routeAPI switched off or address misspelled.
404ec_api_not_foundNo published entry with this ID.
429ec_api_rate_limitedToo many requests within a minute; the Retry-After header gives the wait in seconds.

Data protection

The API only passes on what visitors can see on the website anyway. Drafts, password-protected entries, favorite counters and speakers marked “internal only” are never included; email addresses are off by default.