API

The JSON API provides read-only access to community data without authentication and supports cross-origin requests.

Base URL

Endpoints

EndpointDescription
/api/companiesList companies
/api/companies/:slugGet company
/api/eventsList events
/api/events/:slugGet event
/api/groupsList groups
/api/groups/:slugGet group
/api/jobsList jobs
/api/jobs/:slugGet job
/api/educationList education
/api/education/:slugGet education
/api/newsList news
/api/news/:slugGet news article
/api/peopleList people
/api/people/:slugGet person
/api/projectsList projects
/api/projects/:slugGet project
/api/productsList products
/api/products/:slugGet product
/api/technologiesList technologies
/api/technologies/:slugGet technology

Pagination

List endpoints support pagination:

  • limit - Number of items (default: 20, max: 100)
  • offset - Items to skip (default: 0)

Responses include a pagination object:

{
  "data": [...],
  "pagination": {
    "total": 42,
    "limit": 20,
    "offset": 0,
    "hasMore": true
  }
}

Responses also include RFC 5988 Link headers for navigation:

Link: <https://siliconharbour.dev/api/companies?limit=20&offset=20>; rel="next",
      <https://siliconharbour.dev/api/companies?limit=20&offset=0>; rel="first"

OpenAPI specification

View OpenAPI Spec

MCP server

AI assistants and agents can connect to the MCP (Model Context Protocol) server at /mcp.

Endpoint: https://siliconharbour.dev/mcp

Tools

The server has two public tools and one authenticated tool. Expand a tool to see its available functions. This list comes from the bridge implementation and stays in sync with the server.

searchPublicexpand

Search the SiliconHarbour API schema to discover available entities and field shapes. Call this first to learn what data exists, then use query or execute to fetch it.

queryPublic8 functionsexpand

Execute JavaScript in a secure QuickJS sandbox to read SiliconHarbour data. Imports below are available from the 'siliconharbour' module. Each call hits the real database on-demand. Timeout: 10 seconds.

Read (8)
companies({ query?, limit?, offset? })
List visible companies. Optional query filters by name and description.
education({ limit?, offset? })
List educational institutions and programs.
events({ upcoming?, limit?, offset?, query? })
List events. Pass upcoming:true to limit to future events. Defaults to limit 20.
groups({ limit?, offset? })
List community groups.
jobs({ query?, limit?, offset?, companyId?, sourceId?, status?, isTechnical?, workplaceType?, location?, lastSeenAfter? })
Search jobs with company and import-source identity. Defaults to status:'active' and includes technical and non-technical jobs. status accepts active|pending_review|removed|filled|expired|hidden|all; location is a case-insensitive substring; lastSeenAfter accepts an ISO date.
news({ query?, limit?, offset? })
List published news items. Optional query filters by indexed content.
people({ query?, limit?, offset? })
List visible people from the directory.
technologies({ limit?, offset? })
List technologies referenced across the directory.
executeAuthenticated30 functionsexpand

Like query, but also exposes sync, creation, review, and lifecycle functions. Requires an OAuth-authorized MCP session with the mcp:write scope. Timeout: 60 seconds.

Read (8)
companies({ query?, limit?, offset? })
List visible companies. Optional query filters by name and description.
education({ limit?, offset? })
List educational institutions and programs.
events({ upcoming?, limit?, offset?, query? })
List events. Pass upcoming:true to limit to future events. Defaults to limit 20.
groups({ limit?, offset? })
List community groups.
jobs({ query?, limit?, offset?, companyId?, sourceId?, status?, isTechnical?, workplaceType?, location?, lastSeenAfter? })
Search jobs with company and import-source identity. Defaults to status:'active' and includes technical and non-technical jobs. status accepts active|pending_review|removed|filled|expired|hidden|all; location is a case-insensitive substring; lastSeenAfter accepts an ISO date.
news({ query?, limit?, offset? })
List published news items. Optional query filters by indexed content.
people({ query?, limit?, offset? })
List visible people from the directory.
technologies({ limit?, offset? })
List technologies referenced across the directory.
Import sources (4)
eventImportSources()
List all event import sources with id, sourceType, fetchStatus, lastFetchedAt, and pendingCount.
jobImportSources()
List job import sources with company identity, configuration, fetch health, and counts for every job lifecycle status.
listImporterTypes()
Return the name, approach, reliability, and quirks for every job importer type, including greenhouse, ashby, workday, bamboohr, lever, and custom.
newsImportSources()
List all news import sources (RSS feeds and custom) with enabled flag, lastSyncStatus, and pendingCount.
Synchronous sync (2)
syncAllSources({ type? }) where type is 'event'|'job'|'news' (omit to sync all)
Synchronously sync all sources of the given type sequentially. If type omitted, syncs every event, job, and news source in turn.
syncSource({ type, sourceId }) where type is 'event'|'job'|'news'
Run a sync for a single import source. Dispatches by `type` to the matching sync function.
Background sync (3)
asyncSyncAllSources({ type? }) where type is 'event'|'job'|'news' (omit to sync all)
Start a background sync of all sources of the given type. If type is omitted, queues every event, job, and news source in one run. Returns a runId to poll with getAsyncSync(runId).
getAsyncSync(runId)
Get the live status of a background sync run: { status, completed, failed, current, steps[] }.
listAsyncSyncs()
List recent background sync runs (most recent first, max 20 stored).
Creation (5)
createEntity({ type, ...fields })
Create an entity. `type` dispatches to the matching create flow: person, education, product, project, technology, company, group, event, job, event-source, job-source, news-source, news-article, news-link. Slug auto-generated where applicable; most entities default visible=false so an admin can review before publishing. Per-type field requirements are listed in the execute tool description; also discoverable via search('createEntity <type>').
deleteEntity({ type, id })
Delete an entity by id. Types: person, education, product, project, technology, company, group, event, job, event-source, job-source, news-source. Use with care. There is no undo, and deleting an import source orphans any pending events, jobs, or news that came from it.
deleteEventTag({ id })
Delete an event tag and its event assignments. Events are not deleted.
saveEventTag({ id?, name, color })
Create an event tag, or update one when id is supplied. Colours: harbour, green, amber, red, purple.
updateEntity({ type, id, ...fields })
Patch fields on an entity. `type` dispatches: person, education, product, project, technology, company, group, event, job, news, event-source, job-source, news-source. All fields except `type` and `id` are optional. Only supplied fields are updated.
Lookup / detail (3)
eventTags()
List configured event tags, their colour token, and the number of assigned events.
getEntity({ type, by, value }) where by is 'id'|'slug'|'name'
Look up an entity. `by:'name'` is only supported for type:'company'. Types: company, group, person, education, product, project, technology, job, news, event, event-source, job-source, news-source. Source types support by:'id' only. Returns { found: true, type, entity } or { found: false }.
listEntities({ type, filter? }) where filter is 'manual'|'pending'|'all'
List entities by kind and filter. Types include the 8 directory entities (job, event, news, company, group, person, education, technology) and the 3 source types (event-source, job-source, news-source). filter:'pending' (job, event, news) returns the pending_review queue. filter:'manual' (job, event) returns manually-created entries (sourceType='manual' for jobs, no importSourceId for events). filter:'all' (the default for source types) returns the unfiltered set.
External search (4)
getTechNLJob(link)
Full HTML/text description for one TechNL posting by its link.
listTechNLJobs()
Live technl.ca job board with company-match info so you can spot which postings we already have via createEntity({ type:'job' }) or an importer.
searchIndeedJobs({ query?, location?, limit?, hoursOld? })
Search Indeed via their mobile GraphQL API. Returns job data plus match (known company, existing job/status, configured sources, duplicate confidence) and discoveredSource when a direct application URL identifies a supported ATS. Default location: "St. John's, NL".
searchLinkedInJobs({ query?, location?, limit? })
Search LinkedIn via the public jobs-guest endpoint. Returns job data plus match (known company, existing job/status, configured sources, duplicate confidence). LinkedIn does not expose a direct ATS URL here, so discoveredSource is normally null.
Lifecycle / review (1)
reviewEntity({ type, id, action })
Move a pending entity to its final state. type:'job' actions: approve | approve-non-technical | hide | requeue | deactivate-removed | deactivate-filled | deactivate-expired. type:'news' actions: approve | hide.

Connect an MCP client

Add the endpoint to any client that supports remote MCP servers:

{
  "mcpServers": {
    "siliconharbour": {
      "url": "https://siliconharbour.dev/mcp"
    }
  }
}

You can use search and query without signing in. These tools provide read-only access to public SiliconHarbour data.

OAuth is optional. To use the execute tool, start your MCP client's sign-in action and log in with your SiliconHarbour account. The client discovers the OAuth endpoints automatically and opens the site's login and consent flow. You do not need an API key or shared secret.

The execute tool requires the mcp:write OAuth scope and an administrator account. It can run imports, create or update records, and perform review actions. An invalid or expired OAuth session is rejected instead of falling back to public access.

Example queries

// Upcoming events
import { events } from "siliconharbour";
export default await events({ upcoming: true, limit: 5 });
// Jobs filtered by keyword
import { jobs } from "siliconharbour";
export default await jobs({ query: "developer", limit: 10 });
// Cross-entity query
import { events, companies, groups } from "siliconharbour";
const [upcoming, cos, grps] = await Promise.all([
  events({ upcoming: true }),
  companies({ limit: 5 }),
  groups({ limit: 5 }),
]);
export default {
  upcoming: upcoming.length,
  companies: cos.map((c) => c.name),
  groups: grps.map((g) => g.name),
};

Feeds

RSS feeds and an iCal calendar are also available. See Stay Connected for details.

  • /feed.rss - Combined RSS feed
  • /events.rss - Events RSS feed
  • /news.rss - News RSS feed
  • /jobs.rss - Jobs RSS feed
  • /calendar.ics - iCal calendar subscription