Ingest organizations
Status: Proposed 2026-09-29
Organizations are hosts: an event needs one, a sanctuary is one, and a member finds a chapter through one. They are curated, not discovered. ingest:organizations reads scripts/data/organizations.json in the API repo and POSTs to POST /api/ingest/organizations under the contract.
What is in the directory
| Group | Source of truth | type |
|---|---|---|
| Anonymous for the Voiceless chapters in Southern California | the chapter list on the movement's own site | org |
| Animal Save Movement chapters | the chapter directory on the movement's own site | org |
| LA-area organizations: outreach groups, advocacy nonprofits, org-run meetups | each org's own site | org |
| Sanctuaries | scripts/data/sanctuaries.json, the same entries that become places | sanctuary |
| Vegan businesses that host events | their own site, added when they first host | business |
Every entry is checked by a person against the org's own site before it is added. A directory is read for the list of chapters; details always come from the chapter's own page.
Item shape
| Field | Required | Rule |
|---|---|---|
sourceId | yes | stable kebab-case key: afv-long-beach, harbor-animal-save |
name | yes | as the org writes it |
type | yes | org, sanctuary, business |
description | one or two sentences in the app's own words, not copied from the org | |
website | the org's own site, https | |
socials | handles only, see below | |
area | one of the home areas, other when the org is regional | |
sourceUrl | the page the entry was checked against |
source is curated. A sanctuary in sanctuaries.json is also sent as an organization with the same sourceId, which is how a sanctuary can host a volunteer day.
{
"sourceId": "harbor-animal-save",
"name": "Harbor Animal Save",
"type": "org",
"description": "Bears witness at harbor-area slaughterhouses and organizes monthly vigils.",
"website": "https://example.org",
"socials": { "instagram": "harboranimalsave" },
"area": "south_bay",
"sourceUrl": "https://example.org/chapters"
}
Socials
socials holds the organization's official handles as plain handles (instagram: "harboranimalsave"), never URLs and never a person's account. When a chapter's only presence is an organizer's personal profile, leave socials empty and point website at the movement's site. The API stores { instagram, facebook, bluesky, mastodon, tiktok, youtube } and the clients build the links. Facebook here is a handle for an outbound link, not a data source.
Moderation
Every ingested organization lands verified: false unless curated is in TRUSTED_SOURCES. An admin verifies after checking the site. verified never goes back to false through ingest, and adminUserIds is never part of an item or a response.
A stub created by event host resolution (see events) has the org's slug and the event source's provenance. When the directory later adds the same org, the upsert matches the slug, adopts the stub, and sets source: curated, so no duplicate is created.
Cadence
Monthly, and whenever the JSON changes. Chapters open and close, so the monthly run is also when a person rechecks each directory and removes entries whose site is gone. Removal is a pull request; ingest never deletes. A removed entry keeps its row with an aging lastSeenAt until an admin retires it.