Integration API

Last updated October 20, 2026

Another system can keep streams on Monstream for its own objects, such as a research topic, a kernel or a client, and receive what they find. The integration acts as a Monstream account: it sees and changes only what that account could. Base URL: https://monstream.io/api/v1/integrations.

Access

  • Make an account for the system and confirm its email.
  • In Settings → Integrations, make an API key with the scopes it needs. The key is shown once. Monstream keeps only its hash.
  • Send it with every call: Authorization: Bearer ms_live_…. Scopes: streams:read, streams:write, finds:read, webhooks:write, feedback:write.

A stream for each of your ids

You name a stream by your own id: system plus ref. The call is idempotent, so the same request twice changes nothing. Describe the stream in words and Monstream drafts the rules, or send the rules yourself as config.

PUT /streams/by-ref/fherma/kernel-ckks-bootstrap
{
  "describe": "New work on CKKS bootstrapping: papers, implementations, benchmarks",
  "visibility": "private",
  "sources": ["https://eprint.iacr.org/rss/rss.xml"],
  "sources_only": false
}
  • GET, DELETE, POST …/pause and POST …/resume on the same path. Deleting a stream the API made deletes the stream.
  • To follow a stream that already exists, find it with GET /streams?q=…, then call PUT /bindings/fherma/kernel-x with {"stream_id": "…"}.

Webhooks

POST /webhooks
{
  "name": "FHERMA",
  "url": "https://your-system.example/hooks/monstream",
  "system": "fherma",
  "mode": "auto",
  "auth_header": { "name": "Authorization", "value": "Bearer <your own secret>" }
}
  • The answer carries a signing secret (whsec_…), shown once.
  • auth_header is your own secret. Monstream stores it encrypted and sends it with every call, so your endpoint can turn away everyone else.
  • Before anything is sent, Monstream posts a webhook.verify event. Answer it with its data.challenge in the body.
  • Auto mode sends every new find. Manual mode sends only the finds you send by hand from the stream page.
  • Events: find.created, find.updated, find.removed, stream.updated, stream.deleted.
  • Failed deliveries are retried for about a day. After five deliveries in a row fail completely, the webhook stops until you verify it again.

Check every call's Monstream-Signature: t=…,v1=…. v1 is the hex HMAC-SHA256 of t + "." + raw body, keyed with the signing secret.

import { createHmac, timingSafeEqual } from 'node:crypto';

function fromMonstream(rawBody, header, secret) {
  const m = /^t=(\d+),v1=([a-f0-9]{64})$/.exec(header ?? '');
  if (!m || Math.abs(Date.now() / 1000 - Number(m[1])) > 300) return false;
  const expected = createHmac('sha256', secret).update(m[1] + '.' + rawBody).digest();
  return timingSafeEqual(expected, Buffer.from(m[2], 'hex'));
}

Deduplicate on Monstream-Event-Id. Use find.story_id as the stable id of the story.

Pull

GET /finds?after=<cursor> returns { items, next }. Pass next back as after until it is null. POST /finds/<id>/feedback with relevant or not_relevant tells the stream what fits.

Questions: [email protected].

Integration API · Monstream