ShrubberyDocs
Sign in

POST /api/gather

First-party Smart Paste endpoint. The Squire reads raw text — meeting notes, chat dumps, email threads — and returns one or more Draft Shrubbery candidates with confidence levels. The caller (the in-app Capture surface) chooses which draft(s) to save.

/api/gather is not part of the public webhook surface. It authenticates via the user's Supabase session cookie, not a Bearer token, and there is no programmatic path to it. External integrations should call POST /api/v1/inbox instead.

⚠️ Try-it caveat. Sprint 37b enabled Scalar's interactive Try-it client on /api-reference. Firing the gather operation from there sends a real request through your active session — which consumes a real BYOK token call against your nominated provider (OpenAI / Anthropic). Each try-it execution costs you the same as a Smart Paste from inside the app. The inbox + sync operations have no per-request cost (just a database write), so try-it is free-to-explore there.

Quick start

The endpoint is consumed by the in-app Smart Paste surface; direct curl calls require a valid session cookie. For programmatic testing during development:

curl -X POST http://localhost:3000/api/gather \
  -H "Cookie: sb-access-token=$SESSION_TOKEN" \
  -H "Content-Type: application/json" \
  -d '{
    "text": "Alice, please ship the Q3 report by Friday. Bob can review."
  }'

A successful response is 200 OK with a drafts array, the upstream model name, and token usage as reported by the provider.

Reference

Notes

  • BYOK required. The endpoint resolves the caller's stored provider key (OpenAI or Anthropic) and calls the upstream LLM with that key. If no key is configured, 400 no_byok_key is returned with a message directing the user to /settings/integrations. There is no platform-side fallback after Sprint 14 — this is a load-bearing privacy property, not a billing optimisation.
  • No row written. /api/gather only extracts and returns drafts. Persisting a draft as a Shrubbery row is a separate step in the Capture flow (see Capture Flow).
  • Rate limits. Per-user limits via Upstash Ratelimit. On exceed, 429 rate_limited is returned with a Retry-After header in seconds. The limit window and burst are environment-configured; the response message rounds up to whole minutes for human display.
  • Upstream failures. If the provider returns an error (invalid key, quota exhausted, model unavailable), 502 upstream_failure is returned with the provider's message surfaced verbatim. Treat this as an opaque debugging string, not a stable error code.
  • No prompt injection mitigations beyond the system prompt. The extracted output is structured JSON validated against DraftShrubberiesResponse; any extraction the model refuses to make returns drafts: [] rather than an error.

Last updated: 17 May 2026