Guide · 4 min read

Ingestion API 101

Everything your memory can eat today — documents, transcripts, and the endpoints behind them.

2026-06-11

What ingestion means in Petals

When you add something to your memory — a note, a meeting transcript, a document — Petals doesn't just store it. It reads it. Background workers extract entities (people, places, ideas), surface claims, and weave the result into your memory graph. What started as raw text becomes a set of nodes and relationships your assistants can reason about.

That pipeline is called ingestion. The in-app forms are the manual lane. The two endpoints documented here are the programmatic lane — the same pipeline, reached over HTTP.

Authentication

All ingestion endpoints authenticate via API key. Create one at Settings → API keys in the app; every key starts with the prefix petals-.

Pass the key in either of these headers — both work:

text
x-api-key: petals-yourkey... Authorization: Bearer petals-yourkey...

The x-api-key header is the shorter form; Authorization: Bearer is there for tools that expect OAuth-style bearer tokens. Pick whichever fits your environment and stick with it.

Before you start

Create an API key at Settings → API keys, then set it as an environment variable:

bash
export PETALS_API_KEY="petals-yourkey..."

The examples below read from process.env.PETALS_API_KEY. That is the only setup required before any script can talk to the ingestion endpoints.

The manual lane, in-app today

Before writing a single line of script, check whether the in-app path covers your case. The Add-to-Memory dialog (the + button in the toolbar) handles:

  • Notes — free-form text with optional title, author, date, and scope
  • Files — PDF, DOCX, DOC, RTF, TXT, Markdown, HTML, up to 10 MB per file; multi-file batch supported
  • Transcripts — raw text (server-side LLM segmentation) or pre-segmented utterances; best-effort parsing for Otter, Granola, and Zoom exports

If your source material is something you can export and upload manually, start there. The API earns its place when you need to automate — daily summaries, recurring feeds, custom pipelines.

The two endpoints

POST /api/memory/ingest/document

Use this when your content is a discrete piece of text: a note, an article, a day's summary, a journal entry.

Request:

json
{ "document": { "id": "my-journal-2026-06-11", "content": "Spent the morning reviewing the ingestion API surface...", "contentType": "markdown", "title": "Journal — June 11", "scope": "personal", "timestamp": "2026-06-11T09:00:00Z" }, "updateExisting": false }

Field notes:

  • id is your stable identifier. Re-sending the same id with updateExisting: true replaces the existing entry rather than creating a duplicate.
  • contentType defaults to "markdown". Use "text" for plain prose, "html" if your source is scraped or rendered HTML.
  • scope defaults to "personal". Set "reference" for source material you want your assistants to consult but not conflate with your own memories (a book summary, a company doc).
  • timestamp defaults to the time of ingestion if omitted.

Response:

json
{ "message": "Document ingestion queued", "jobId": "job_01hwxyz...", "sourceId": "src_01hwxyz..." }

The job is queued immediately; the response comes back fast. The actual extraction runs in the background.

POST /api/memory/ingest/transcript

Use this when your content is a conversation — a meeting, a recorded call, a voice note turned to text.

Request — raw mode (let Petals segment the speakers):

json
{ "transcriptId": "standup-2026-06-11", "occurredAt": "2026-06-11T09:30:00Z", "content": { "kind": "raw", "text": "Marcel: Morning. Anything blocking? Alex: No blockers, shipping the guide today. Marcel: Great." } }

Request — segmented mode (you've already parsed the speakers):

json
{ "transcriptId": "standup-2026-06-11", "occurredAt": "2026-06-11T09:30:00Z", "content": { "kind": "segmented", "utterances": [ { "speakerLabel": "Marcel", "content": "Morning. Anything blocking?" }, { "speakerLabel": "Alex", "content": "No blockers, shipping the guide today." }, { "speakerLabel": "Marcel", "content": "Great." } ] }, "knownParticipants": [{ "label": "Alex", "nodeId": "node_01..." }] }

knownParticipants links a speaker label to an existing Person node in your graph. Petals uses this to attribute claims to the right person rather than creating a duplicate node.

Response:

json
{ "message": "Transcript ingestion queued", "jobId": "job_01hwxyz..." }

Transcript responses return a jobId only — no sourceId. The transcript becomes a source once processing completes.

What async means for you

Both endpoints respond immediately with a jobId. The actual work — entity extraction, claim surfacing, graph updates, semantic embedding — happens in background workers. On a small document, the graph typically reflects the new content within a few seconds. On a long transcript or a dense file, it can take longer.

You cannot poll job status via the public API today. The simplest check is to open your memory explorer and look for new nodes. More programmatically: re-run a search that should surface the content and wait for it to appear. A future jobs endpoint is the cleaner answer; for now, build pipelines that tolerate the delay rather than block on it.

A minimal script skeleton

javascript
// pattern only — adapt to your environment // create your API key at Settings → API keys const API_KEY = process.env.PETALS_API_KEY; const BASE_URL = "https://petals.chat"; async function ingestDocument(doc) { const res = await fetch(`${BASE_URL}/api/memory/ingest/document`, { method: "POST", headers: { "Content-Type": "application/json", "x-api-key": API_KEY, }, body: JSON.stringify({ document: doc, updateExisting: true }), }); if (!res.ok) { const err = await res.json(); throw new Error(`Ingestion failed: ${err.error ?? res.status}`); } return res.json(); // { message, jobId, sourceId } }

What's coming

Native connectors for continuous ambient feeds — screen capture tools, wearables, note-sync services — are the next unlock. The script pattern above is already a proper integration; automated connectors will remove the need to maintain the script at all.

The endpoints are stable and the graph pipeline is real. The path from external data to your memory graph runs through a key you already have.