Processors
What a feed carries is decided by processors and filters. You set both up through the API, for event feeds and news feeds alike.
- Processor — looks at each item in your feeds and records answers ("outputs"); for example Event labels
records
age_restricted.confidence: 0.92. A processor never includes or excludes anything. Built-in processors are ours; you can create your own. A processor belongs to your organization, not to a feed. - Filter — a rule on one feed: include or exclude items whose processor outputs meet a condition. An item is in the feed when no exclude filter matches and at least one include filter does. A feed runs exactly the processors its filters read.
This page covers processors. Filters covers the rules, Opt-in filters our ready-made ones, Areas the area processors and Sources what a feed's items come from.
A processor's answers are read by filters only. They are not part of feed items, and the API never returns them.
Built-in processors
Our built-ins are usable by every organization and editable by no one. A
filter names a built-in by its key. GET /processors/{key} returns a
built-in with every output, its range and what it means.
| Key | Runs on | Outputs |
|---|---|---|
completeness | events | score, 0 (bare) to 1 (everything present) |
event_labels | events | For each of 11 labels, <label>.answer (yes or no) and <label>.confidence (0 to 1). Labels: adult_content, age_restricted, promotional, not_an_occasion, virtual_only, fundraiser, religious_service, political_campaign, civic_government, business_professional, family_friendly |
event_category | events | primary (one of 17 categories), primaryConfidence (0 to 1), secondary (a category or none), secondaryConfidence (0 to 1) |
has_cover | events | hasCover (true or false) |
has_official_source | events | hasOfficialSource (true or false) |
price | events | price: free, paid or unknown |
news_content_type | news | For each of 14 content types, <type>.significance (0 to 10) and <type>.confidence (0 to 1) |
The 17 event categories are music, performing_arts, visual_arts,
film, food_drink, markets_fairs, festivals, sports_recreation,
fitness_wellness, family_kids, education_talks,
community_volunteering, civic_government, nightlife,
business_networking, religious_spiritual and other. The 14 news content
types are listed in
News Feeds Concepts. How labels and
categories are judged is in
Event Feeds Concepts.
The safety floor is a processor too, but it is not listed and no filter of yours can read it: it applies to every feed and cannot be switched off.
Your processors
You create a processor from a template:
| Template | What it answers | Outputs |
|---|---|---|
custom | A question of your own. A classifier answers yes or no; a scorer scores the item on your rubric. | Classifier: answer (yes/no) and confidence (0 to 1). Scorer: score (1 to the number of rubric levels) |
area_match | Whether the item's places fall inside an area, by their boundaries. No AI. See Areas. | match: match, no_match or unknown |
area_relevance | Whether the item takes place in or is about an area, judged by AI from its text. See Areas. | answer and confidence |
event_relevance | Whether an event belongs in a feed, from the feed's title, description and your rules. For a new question, prefer custom. | answer and confidence |
news_relevance | Whether a news item belongs in a feed, from the feed's title, description and your rules. For a new question, prefer custom. | answer and confidence |
confidence is how confident the processor is in a "yes", from 0 (not at
all) to 1 (certain).
A processor belongs to your organization, so any of your feeds can use it.
custom processors run on one item type (itemTypes: ["event"] or
["news"]), relevance processors on their own type, and area processors on
both by default, so one area serves your event and news feeds.
We choose and run what is behind each processor. The API does not show or accept a provider or model.
Definitions
Each template has its own definition:
custom:instructions(the question, up to 4,000 characters), the four rule and example lists (below) and, for a scorer only (and required for it),rubric: 2 to 10 levels{ name, description }, lowest first. The score is the level the item reaches, from 1.area_match:area: { name, places }. See Areas.area_relevance:areaas above, plus the four lists.event_relevanceandnews_relevance:feed: { title, description },geographyDecidedSeparatelyand the four lists.
The four lists are positiveRules (rules for a "yes"), negativeRules
(rules for a "no"), positiveExamples and negativeExamples (example items
of each), up to 50 entries of up to 1,000 characters each. A list left out is
empty.
geographyDecidedSeparately says who judges where items take place. false
(the default) and the relevance processor judges geography too, from the
feed's title and description. true and it judges only the topic: an area
decides where. Set it to true when a filter combines the relevance
processor with an area, so geography is not decided twice. The relevance
parameter of our opt-in filters requires it
of an event_relevance or news_relevance processor; a custom classifier
has no such flag and is taken as it is.
Changing a processor
PATCH /processors/{id} changes name, description or definition. The
template, kind and item types cannot change.
definitionreplaces the whole definition. Send every rule and example list you want to keep: a list you leave out becomes empty. OnlygeographyDecidedSeparatelykeeps its value when you leave it out.- A changed definition makes a new
version. It applies going forward only: to new items, and to items already in a feed when they next change. Items are not re-judged when you save. A name or description change makes no version. - An edit that would break a filter is refused. If a filter reading the
processor would no longer be valid (for example, a scorer's rubric shrinking
from 5 levels to 3 under a filter
score >= 4), the PATCH is a409PROCESSOR_EDIT_BREAKS_FILTERSnaming those filters and what is wrong with each. Nothing is saved. - A definition its template or kind refuses is a
422VALIDATION_FAILED(for example a scorer without a rubric), with each problem indetails.problems[{ path, code, message }], as on create. Nothing is saved. (A filter's condition that does not check out is a different code,422INVALID_CONDITION; see Filters.) - Built-ins cannot be changed:
403BUILT_IN_PROCESSOR.
Archiving a processor
DELETE /processors/{id} archives the processor. It stays readable, with
archivedAt, but runs for no filter and cannot be changed (409
PROCESSOR_ARCHIVED). Archiving is refused while any filter reads the
processor, including an opt-in filter that takes it as a parameter: 409
PROCESSOR_READ_BY_FILTERS, with the filters in details.filters. Remove
those filters, or change them to read another processor, first.
{
"success": false,
"error": {
"code": "PROCESSOR_READ_BY_FILTERS",
"message": "Processor '5d1c7a2e-9b4f-4c3e-8a6d-1f2e3d4c5b6a' is read by 1 filter(s); remove them or change them to read another processor first.",
"details": {
"filters": [
{
"feedType": "event",
"feedId": "550e8400-e29b-41d4-a716-446655440000",
"filterId": "7f3c2b1a-6e5d-4c4b-9a3f-2e1d0c9b8a71",
"name": "In the area"
}
]
}
}
}
API reference
| Route | Does |
|---|---|
GET /processors | Our built-ins, then your processors by name. ?itemType=event or news; ?includeArchived=true |
GET /processors/{id} | One processor, archived or not. A built-in also by its key: /processors/event_labels |
POST /processors | Create one of your processors |
PATCH /processors/{id} | Change its name, description or definition |
DELETE /processors/{id} | Archive it |
Every route needs your API key. Another organization's processor is a 404.
The processor
| Field | Description |
|---|---|
id | Stable identifier: what a filter's condition names. Built-ins are named by key. |
key | Built-ins only: the key, e.g. event_labels. |
owner | platform (ours) or organization (yours) |
name | Unique in your organization, archived processors included |
description | What the processor is for |
itemTypes | event, news or both |
kind | classifier (records answers) or scorer (records a score on a scale) |
template | Yours only: the template |
definition | Yours only: the definition, an area with its places resolved |
version | The version of its definition, from 1 |
outputs | What a filter can read: each with name, type (number, boolean or enum), min and max or values, and description |
createdAt, updatedAt, archivedAt | ISO 8601; archivedAt is null unless archived |
Create a processor
POST /processors
{
"template": "area_match",
"name": "Charlotte metro",
"definition": {
"area": {
"name": "Charlotte metro",
"places": [
{ "name": "Mecklenburg County, North Carolina" },
{ "name": "Gaston County, North Carolina" },
{ "name": "Rock Hill, South Carolina", "level": "place" }
]
}
}
}
| Field | Required | Description |
|---|---|---|
template | Yes | custom, area_match, area_relevance, event_relevance or news_relevance |
name | Yes | Up to 200 characters, unique in your organization |
description | No | Up to 1,000 characters. An area processor gets one written for it when left out |
kind | custom only | classifier or scorer |
itemTypes | custom only | ["event"] or ["news"]. Area processors take ["event"], ["news"] or both (the default); relevance processors take none |
definition | Yes | The template's definition (see Definitions) |
Response 200 OK:
{
"success": true,
"data": {
"id": "5d1c7a2e-9b4f-4c3e-8a6d-1f2e3d4c5b6a",
"name": "Charlotte metro",
"description": "Whether the item's places fall inside Charlotte metro",
"itemTypes": ["event", "news"],
"kind": "classifier",
"owner": "organization",
"template": "area_match",
"definition": {
"area": {
"name": "Charlotte metro",
"places": [
{
"type": "place",
"id": "mbx_WyJkWEp1T20xaWVIQnNZenBCVmtKdk4xRSIsImRpc3RyaWN0IiwiTWVja2xlbmJ1cmcgQ291bnR5LCBOb3J0aCBDYXJvbGluYSwgVW5pdGVkIFN0YXRlcyJd",
"name": "Mecklenburg County",
"level": "district",
"displayText": "Mecklenburg County, North Carolina, United States",
"geo": {
"center": { "lat": 35.247, "lng": -80.833 },
"bbox": [-81.058, 35.002, -80.55, 35.515]
}
},
{
"type": "place",
"id": "mbx_WyJkWEp1T20xaWVIQnNZenBCVkhCdk4xRSIsImRpc3RyaWN0IiwiR2FzdG9uIENvdW50eSwgTm9ydGggQ2Fyb2xpbmEsIFVuaXRlZCBTdGF0ZXMiXQ",
"name": "Gaston County",
"level": "district",
"displayText": "Gaston County, North Carolina, United States",
"geo": {
"center": { "lat": 35.294, "lng": -81.178 },
"bbox": [-81.456, 35.152, -80.957, 35.416]
}
},
{
"type": "place",
"id": "mbx_WyJkWEp1T20xaWVIQnNZenBHUmxOdk4xRSIsInBsYWNlIiwiUm9jayBIaWxsLCBTb3V0aCBDYXJvbGluYSwgVW5pdGVkIFN0YXRlcyJd",
"name": "Rock Hill",
"level": "place",
"displayText": "Rock Hill, South Carolina, United States",
"geo": {
"center": { "lat": 34.925, "lng": -81.025 },
"bbox": [-81.12, 34.87, -80.94, 34.99]
}
}
]
}
},
"version": 1,
"outputs": [
{
"name": "match",
"type": "enum",
"values": ["match", "no_match", "unknown"],
"description": "Whether the item is inside the area: match, no_match (outside it), or unknown (no known location)."
}
],
"createdAt": "2026-10-28T14:05:00.000Z",
"updatedAt": "2026-10-28T14:05:00.000Z",
"archivedAt": null
}
}
A custom classifier, for comparison:
{
"template": "custom",
"name": "Family friendly",
"kind": "classifier",
"itemTypes": ["event"],
"definition": {
"instructions": "Is this event suitable for families with young children?"
}
}
Update a processor
PATCH /processors/{id}
Send only the fields to change. definition is the whole new definition.
{
"name": "Charlotte metro",
"definition": {
"area": {
"name": "Charlotte metro",
"places": [
{
"id": "mbx_WyJkWEp1T20xaWVIQnNZenBCVmtKdk4xRSIsImRpc3RyaWN0IiwiTWVja2xlbmJ1cmcgQ291bnR5LCBOb3J0aCBDYXJvbGluYSwgVW5pdGVkIFN0YXRlcyJd"
},
{ "name": "Union County, North Carolina" }
]
}
}
}
Returns the processor, as on create.
Archive a processor
DELETE /processors/{id}
Returns { "success": true }. Archiving an archived processor changes
nothing.
Errors
| Status | Code | When |
|---|---|---|
400 | INVALID_BODY | The body does not match the template's shape |
403 | BUILT_IN_PROCESSOR | A PATCH or DELETE of a built-in |
404 | NOT_FOUND | No such processor, or another organization's |
409 | ALREADY_EXISTS | Your organization already has a processor with that name, archived ones included |
409 | PROCESSOR_ARCHIVED | A PATCH of an archived processor |
409 | PROCESSOR_READ_BY_FILTERS | Archiving while filters read it. details.filters[{ feedType, feedId, filterId, name }] |
409 | PROCESSOR_EDIT_BREAKS_FILTERS | The new definition would make filters reading it invalid. details.filters as above, and details.problems[{ filterId, path, code, message }] |
422 | AREA_PLACES_UNRESOLVED | A place name is not exactly one place. See Areas |
422 | VALIDATION_FAILED | The definition does not suit its template or kind (for example a scorer without a rubric). details.problems[{ path, code, message }] |
429 | RATE_LIMITED | More than 30 requests that search places in a minute by your organization. See Areas |
503 | SERVICE_UNAVAILABLE | Place search is down, or its requests cannot be counted against the limit. Retry after the Retry-After header's seconds |