Plum / developers

Suggestions and pipelines

Grounded suggestions, independent pipeline feeds, and shared user context.

A Suggestion is a durable idea for something useful to do. Optima owns its identity, revision history, feedback and action state regardless of which pipeline produced it. Recorded words and generated text are data, never instructions to a client.

Automatic processing

The internal pipeline automatically processes Turns for every confirmed, active account with a ready internal pipeline. It starts at the beginning of recorded history when no checkpoint exists, then resumes from the last successfully processed input. Work runs in bounded batches; later batches can use already synthesized Memories and Conversations. Late-arriving Turns and corrections are tracked separately so older timestamps do not cause them to be skipped. Historical catch-up can take time before the feed reflects the most recent recordings.

Automatic processing saves and updates Suggestions. It does not create webhook subscriptions, send user notifications, or wake a connected harness. Clients read the feed through API/MCP or consume their existing account-event subscriptions. Staging and production process their own database inputs independently.

After a generated batch passes its complete response schema, Optima validates each optional Memory and Suggestion candidate independently. A candidate with invalid evidence, references, identity, or timing is omitted together with candidates that depend on it, while independent grounded results and the processed-input checkpoint can still publish. Malformed or truncated responses, provider failures, stale inputs, and repair failures reject the whole attempt and leave the checkpoint in place. Internal rejection diagnostics are bounded and contain no generated wording. Only resources that publish emit their ordinary account events; rejected candidates do not create events.

The automatic producer uses the account's original internal pipeline. Creating or selecting an experimental pipeline does not start another automatic producer. Cloudflare Agent Memory remains unavailable until its backend is enabled.

Select a pipeline

GET /pipelines lists the available selection. An account has one default pipeline. Omit pipeline_id from suggestion lists, search and recall to use that default. Responses include the resolved pipeline ID even when no results exist. Include it in cache keys and reuse it while paginating. Switching the default invalidates a cursor that was created for the old default; explicitly pin the original pipeline to continue that feed.

Developer-enabled accounts can compare pipeline instances by supplying pipeline_id. Each instance has independent suggestions, feedback, grouping and learned preferences. The same idea can legitimately appear in two feeds. Completing, dismissing or editing it in one feed does not change the other. Shared Turns, Contacts and declared profile context remain the same inputs.

POST /pipelines creates an experimental instance. PATCH /pipelines/{id} changes its label, status or default selection using expected_updated_at and an idempotency_key. A pipeline instance persists across processing jobs. Use a new instance for a clean experiment; changing labels does not clear prior state. A backend's unavailable or disabled status must not be treated as an empty successful analysis. Selecting an unavailable recall backend returns 503, with no fallback to another pipeline.

Non-default pipelines require the pipelines:experimental permission, which comes with developer access: creating or updating a pipeline, listing every pipeline, or selecting a non-default one with pipeline_id otherwise returns 403. Without it, GET /pipelines lists only the default pipeline. With developer access, a token scoped pipelines:read, or with the scopes recall needs (turns:read and insights:read), also lists every pipeline and selects a non-default one; creating or updating still needs pipelines:write. Optima staff grant developer access; users cannot grant it themselves, and OAuth scopes do not grant it on their own. Access only permits inspection of the account's own data.

Read suggestions and evidence

GET /suggestions defaults to the active feed. Use view=history for completed, dismissed, withdrawn and other historical items. Pagination fixes an eligibility cutoff; refresh after a suggestion event to see newer changes. List items include the action, timing, roles, state, review status, grouping and presentation hints, without Turn evidence. evidence_started_at and evidence_ended_at give the capture time spanned by the cited Turns, such as the meeting a suggestion came from; each is null when that capture time is unknown. created_at and updated_at record processing, not when the conversation happened. Open GET /suggestions/{id} for the full current item and its canonical Turn evidence; revision selects immutable content. Fetch the detail before judging whether a suggestion is grounded or acting on its citations.

The full suggestion includes pipeline_id, action, why_now, basis, evidence, optional timing, presentation, mutable state and a state version. Evidence is an array of canonical Turn references, each with source, the state of the cited Turn's transcript:

{
  "turn_id": "fde322ec-07d5-44fd-9e16-cb4b5fdab007",
  "word_start": null,
  "word_end": null,
  "source": { "state": "available", "removed_at": null }
}

Null offsets cite the whole Turn. Otherwise offsets are zero-based, start-inclusive and end-exclusive word indices. When source.state is expired or deleted, the cited words are gone: GET /turns/{id} returns 410 for that Turn, and the suggestion keeps its own action and reasoning. Public Suggestions do not require Memory IDs. Pipeline-specific memories and conversations are developer diagnostics, with one exception: a shared recorder's session summary gives the session's host their titles and text, for that session only, with insights:read.

A suggestion the workspace's retention removed is left out of GET /suggestions (both views), and reading, editing, giving feedback on, or grouping it returns 410 CONTENT_EXPIRED or CONTENT_DELETED with removed_at; see Removed content.

Source changes can mark suggestions for review or restrict evidence reads. Do not retain deleted source text in a client cache.

roles distinguishes speaker, mention, actor and recipient. A role can have a contact ID, detected speaker ID, both or neither. wording preserves an unresolved label; basis identifies detected, mentioned, inferred or user-supplied identity. Recording ownership does not identify the speaker.

States are suggested, accepted, completed, dismissed and withdrawn. Acceptance records the user's choice; it does not perform an outside action. New evidence about an accepted item can set needs_review instead of silently completing or withdrawing it.

Presentation hints

The pipeline classifies each suggestion when it writes the revision, so clients can style and route cards without classifying the action text themselves. List items and full suggestions carry the same presentation object:

{
  "presentation": { "domain": "engineering", "intent": "review", "handoff": "codex" }
}
  • domain is the work area: engineering, product (product and design), business (sales and partnerships), research (customer and user research), or general for work that spans several areas or matches none.
  • intent is the requested interaction: do, decide, review or follow_up. It is independent of needs_review; a suggestion flagged for review keeps its own intent.
  • handoff is where a user-reviewed draft of the action fits best: claude, codex, linear or slack. It is a default, not an instruction. Offer every destination you support, and create or send nothing without the user.

presentation is null on revisions written before hints existed and on pipelines that do not produce them. Each field can gain values; treat an unrecognized value like a missing hint and use your own fallback, such as general styling. Hints belong to a revision. A user edit keeps the current hints, and PATCH cannot set them. They never change state, review status or ordering. A hint change publishes a new revision with the ordinary suggestion.updated event; event metadata never includes hint values.

Edits and feedback

PATCH /suggestions/{id} edits action, why_now, roles or due. Supply expected_revision, expected_state_version and a UUID idempotency_key. Edits create an immutable revision and protect the changed fields from later model overwrite. The revision keeps the current presentation hints. Conflicts return 409.

POST /suggestions/{id}/feedback accepts accept, complete, dismiss, snooze, wrong, irrelevant and more_like_this. For example:

{
  "shown_revision": 2,
  "expected_state_version": 3,
  "idempotency_key": "bd28c730-b5ca-4e2c-a54b-af149cd9d15c",
  "action": "accept"
}

Snooze requires a future snoozed_until; wrong and irrelevant may include reason. Wrong disputes correctness. Irrelevant hides the occurrence without asserting that its evidence is false. More-like-this is a relevance signal, not factual confirmation. These signals belong to the suggestion's pipeline.

An ID-based read or mutation uses that Suggestion's own pipeline even if the default has changed. An explicit mismatched pipeline_id is rejected. Reuse an idempotency key only for an identical retry. Refetch after conflicts and before further writes following a replayed receipt.

POST /suggestions/grouping creates or changes a presentation group, merges or splits duplicates, or records keep_separate / allow_grouping. Supply member IDs and expected revisions, plus the group version where applicable. Members must all belong to the same pipeline. Grouping related suggestions does not share their completion state.

Shared profile

GET /profile returns versioned declared_context: self_contact_id, role, current priorities, useful vocabulary and stated preferences. PUT /profile replaces these fields with expected_revision and idempotency_key. Revision zero is the first write. All pipelines see declared context; inferred preferences remain separate per pipeline and are available only in developer diagnostics.

Search and recall

GET /search?q=...&pipeline_id=... returns text with canonical Turn evidence and a cursor. POST /recall accepts query, optional pipeline_id, and limit, and returns retrieved context or an answer with evidence. Check mode.

The internal backend currently uses full-text retrieval. Search reports mode=full_text; recall reports mode=retrieval, answer=null, and truncated when more matches exist. This is not semantic ranking or an LLM-generated answer. Use concrete search terms. An unavailable backend returns 503 rather than borrowed results. Reading these endpoints does not start inference or incur model charges. Search and recall leave out Turns whose transcript expired or was deleted.

Permissions and events

Suggestions are the insights layer: reading them needs insights:read, and editing, grouping, or giving feedback needs insights:write. Search and recall read turns and insights, so they need turns:read and insights:read. The other MCP scopes are pipelines:read, pipelines:write, profile:read and profile:write. Reconnect and approve new scopes; existing grants gain no new authority automatically. A grant issued with an earlier scope name keeps working. Raw Turn and audio APIs retain their own permissions.

Suggestion events include pipeline_id in bounded metadata. Pipeline creation and selection changes emit pipeline.created / pipeline.updated. Declared profile changes retain the memory_context.updated event name. Older retained events may lack a pipeline ID. Events never include suggestion wording, profile contents or transcripts. See Account events.

On this page