2026-08-15

Keep the catalog in sync

Open-source projects are a subset of the orgs this catalog serves. OSS.Fund is the sibling directory for OSS funding platforms (Open Collective, GitHub Sponsors, Liberapay, and the rest). They do not offer an HTTP API or webhooks. Their public source is Markdown on GitHub (CC BY 4.0): oss-fund/directory.

This site therefore:

  1. Lists OSS.Fund itself as a meta-directory.

  2. Imports live OSS-friendly / nonprofit-tagged platforms into the Open source category (pnpm catalog:sync-oss-fund).

  3. Publishes a pull API so they — or anyone — can consume our catalog.

  4. Offers outbound catalog.updated webhooks, plus an inbound GitHub receiver if OSS.Fund (or we) attach a repo webhook later.

Pull the catalog

JSON feed (CORS enabled, 5-minute cache):

GET https://nonprofit-resources.org/api/catalog

Each item includes id, slug, name, href, category, tags, portalUrl, openSource, and verification flags. Filter items where openSource is true for the OSS subset.

Attribution

Imported OSS.Fund rows stay attributed in each listing and on /attributions. If you republish those rows, keep the CC BY 4.0 credit to OSS.Fund.

Outbound webhooks

Staff (signed-in admin email or CATALOG_WEBHOOK_ADMIN_TOKEN) can register a destination:

curl -X POST https://nonprofit-resources.org/api/webhooks \
  -H "Authorization: Bearer $CATALOG_WEBHOOK_ADMIN_TOKEN" \
  -H "Content-Type: application/json" \
  -d '{"url":"https://example.org/hooks/nr","note":"oss.fund mirror"}'

The response includes a secret once. Every later POST is HMAC-SHA256:

  • Header X-Nonprofit-Resources-Event: catalog.updated

  • Header X-Nonprofit-Resources-Signature: sha256=<hex>

  • JSON body with catalog (URL of /api/catalog) and occurredAt

Production deploys call /api/webhooks/dispatch with CATALOG_WEBHOOK_DISPATCH_SECRET. Polling /api/catalog is enough if you do not want a push.

Private/loopback URLs are rejected.

Inbound GitHub webhook (OSS.Fund)

If the maintainers of oss-fund/directory add a webhook:

That ping starts GitHub Actions (repository_dispatch: oss-fund-sync), which regenerates src/lib/oss-fund-seed.generated.ts and opens a PR. A daily cron does the same when no webhook is attached.

Until they add that hook, we stay in sync by cloning their repo on a schedule.

Sending listings the other way

OSS.Fund accepts new platforms as a pull request against posts/ in oss-fund/directory. A listing for Nonprofit Resources belongs there as a directory, not a funding platform, and should wait on their editorial review. Prefer that they poll /api/catalog if they want the full feed.

  • Browse locally: /services?oss=1

  • Script: pnpm catalog:sync-oss-fund

  • OSS.Fund about page: oss.fund/about