---
name: stargazer
description: Make grounded podcast episodes from documents, either by connecting Stargazer to a chat client or by driving its /v1 API. Spends the owner's prepaid credits; never touches money.
---

# Stargazer for agents

Stargazer turns documents into a short podcast episode, grounded in what the
documents actually say, and puts it on a private RSS feed. One accepted
episode costs one prepaid credit from your human's subscription (25
per cycle, plus packs of 10 they can buy).

This API spends credits. It cannot buy credits and it cannot touch a card.
When the money side needs attention, tell your human. You cannot fix it and
should not try.

## Setup

Your human creates the key on https://stargazer.vector.garden/account (the "connect your agent"
card). It is shown exactly once. Every call carries it:

    Authorization: Bearer sgk_your_key_here

## Connecting from a chat

If you are talking to a person and they want an episode of what the two of you
are looking at, there is a shorter road than this API. Stargazer answers as an
MCP connector at https://stargazer.vector.garden/mcp. Connect it once and you make episodes from inside
the conversation, with no file to save and nothing for the person to upload.

In Claude Code the person runs two commands, and no key is involved:

    claude mcp add --transport http stargazer https://stargazer.vector.garden/mcp
    claude mcp login stargazer

A browser opens, they sign in with the email on their account, they approve the
connection once, and the setup is finished.

If the person is in claude.ai, walk them through this instead. It is a settings
dialog, not a command, so read it out a step at a time and let them keep up.
Settings, then Connectors under Advanced, then "Add custom
connector". Name it Stargazer and paste https://stargazer.vector.garden/mcp in as the url. Claude probes
the server the moment the url lands and fills the rest in itself: they will see
"Detected" against the authentication choice and against the hosted client
metadata one, and both stay exactly as detected. Tell them to skip the request
headers section entirely, because that lane is for api keys and this is not it.
They click Add, sign in with the email on their Stargazer account, and open the
sign-in link that arrives by email, checking spam first if it is slow. The link
opens a consent screen, which they approve once. Back in Claude they switch
Stargazer on in that chat's tools menu, and your first tool call asks their
permission, so approving an episode is approving spending one. Connecting again
later replaces the connection rather than adding a second one, so a redo is
always safe.

Claude Desktop reaches the same screen under a different name, and it is
shorter: Settings, then Extensions, then "Browse Extensions", which is where
its connectors live. Add, name it Stargazer, paste https://stargazer.vector.garden/mcp
into "Remote MCP server URL", and Continue. It runs the sign-in and the consent
itself from there.

Codex takes the same address through its plugin list: Plugins, then Add, then
MCP server. It asks for the transport before it will take an address, so pick
"Streamable HTTP", give it the name and the address, and save. Stargazer then
appears in the list of MCP plugins with an Authenticate button beside it, and
that click is the sign-in, after the server is saved rather than during.

If you are headless, or running in CI where no browser can open, send the
workspace key on the same header every other call here uses and skip the login
step:

    claude mcp add --transport http stargazer https://stargazer.vector.garden/mcp --header "Authorization: Bearer sgk_your_key_here"

Four tools come with it: generate_episode, get_episode_status, list_episodes
and get_feed_url. A submit answers straight away with an episode id, so the
polling rule is the one below, unchanged: every 20 to 30 seconds, never in a
tight loop, and stopping early loses nothing because the episode reaches the
feed either way. The feed address it hands back is a credential, so give it to
the person who asked and put it nowhere else.

Signing in works now: claude.ai custom connectors, Claude Desktop, Claude Code
and Codex all connect by signing in with the email on the account, and any
client that sends a Bearer header connects the way it always did. ChatGPT
developer mode has not been tested against this endpoint yet, so treat it as
untested rather than supported.

## Check before you spend

GET /v1/workspace answers with caps, voices, usage and credits:

    curl https://stargazer.vector.garden/v1/workspace -H "Authorization: Bearer sgk_your_key_here"

Read two numbers before submitting anything:

- credits.total is how many episodes the account can still make. Zero means
  stop and tell your human, not retry. On a workspace with no subscription
  behind it the whole credits block is absent, which is not the same as zero:
  nothing is metered there, so submit normally.
- episodesToday.used against episodesToday.limit is the daily cap. A refused
  body costs nothing, but a refusal that gets as far as the credit check has
  already taken a slot.

The same answer lists the shapes you may ask for and the caps you must stay
under, so read it once at the start rather than guessing.

Never log, repeat or forward the feedUrl it hands back. Its address is the
only thing protecting that feed, so it is a credential like the key is.

## Make an episode

    curl -X POST https://stargazer.vector.garden/v1/episodes -H "Authorization: Bearer sgk_your_key_here" -H "Content-Type: application/json" -d '{"title": "What the handbook says", "sources": ["https://example.com/handbook.pdf"]}'

A 202 means the pipeline started, and the body carries episodeId, the
balance after the spend, and a using block echoing the settings the run will
apply. Check that block: an unknown field is ignored rather than refused, so
a misspelled setting is invisible until you read back what was accepted.

Sources can be https urls or inline objects like {"text": "..."}. Each one
has to clear the minSourceChars floor. Sending an identical body again
returns the existing episode with deduped: true and spends nothing, so a
retry is always safe; a fresh submit has no deduped field at all rather than
a false one.

Order your sources deliberately. Citation markers are numbered in the order
you supply them, so [S1] means your first source permanently, and the script
tends to argue along the sequence you chose.

Four optional fields are worth knowing:

- shape: one of the shapes GET /v1/workspace lists. conversation is two
  voices working through the material and is the default. briefing is one
  voice, essentials only. critique is two voices both skeptical of the
  document. debate is two voices on opposite sides, and only fits material
  that genuinely has two.
- targetMinutes: length, up to maxTargetMinutes. Over the cap is clamped, not
  refused, and the using block reports the clamped value.
- focus: one sentence naming the question the episode should answer. The
  strongest lever you have on what it argues.
- tone: a phrase for how it should sound.

Voices come in two grades, premium and standard. A paid workspace defaults
to premium, so most submits need no voices field at all. To ask for the
plainer pair, put "voices": "standard" in the body. The workspace answer's
voices field says which grade a plain submit gets.

For a PDF you hold as bytes, POST them to /v1/extract with Content-Type
application/pdf and submit the returned text as an ordinary source.

## Poll and fetch

Most episodes finish in a few minutes; a busy voice provider can
stretch it to several. Poll every 20 to 30 seconds:

    curl https://stargazer.vector.garden/v1/episodes/EPISODE_ID -H "Authorization: Bearer sgk_your_key_here"

This answer is shaped differently from the submit: the row sits under an
episode key, beside a workflow key, while the 202 was flat. Read
episode.status, and key your loop on it and nothing else. It is queued, then
running, then either done or error, and only those last two end the wait.

On done, fetch:

    curl https://stargazer.vector.garden/v1/episodes/EPISODE_ID/audio -H "Authorization: Bearer sgk_your_key_here" -o episode.mp3
    curl https://stargazer.vector.garden/v1/episodes/EPISODE_ID/transcript -H "Authorization: Bearer sgk_your_key_here"

The transcript is plain text, not JSON: each turn is wrapped in a speaker tag
with citation markers inline. Split it on the opening tags. The writer
sometimes closes a turn with the other speaker's tag and the pipeline
tolerates that deliberately, so a strict XML parser will fail on episodes
that are otherwise fine.

On error, the row's error field says what went wrong, and the credit that
funded the run comes back on its own. Nothing to do but read it.

## When you are refused

| code | meaning | what to do |
|---|---|---|
| 401 | the key is wrong, rotated, or revoked | tell your human you need the current key |
| 400 | a field is wrong; the error names it | fix the body and resend |
| 400 invalid JSON body | nothing parsed, so no field can be named | check the body is one JSON object, not two concatenated |
| 400 on a short source | a source is under minSourceChars | give it more material or drop it |
| 402 E_NO_CREDITS | the account is out of episodes | stop and tell your human; buying happens in a browser |
| 402 E_PAST_DUE | the card behind the subscription is failing | stop and tell your human |
| 403 E_NOT_ACTIVE | the subscription has ended | tell your human |
| 403 E_FEED_RETIRED | the feed is switched off | tell your human; resubscribing revives it |
| 403 E_VOICES_UNAVAILABLE | that voice grade is not on this workspace | send no voices field, or the other grade |
| 413 | a document is over the size cap | split or trim it; the caps are in GET /v1/workspace |
| 429 | the daily episode limit | wait for the UTC day to roll over |

Never loop on a 402 or 403. Those refusals do not change until a person
acts, and every attempt still burns a slot of the daily limit.

## Buying without a key

If you have no key and no account, you can still buy one episode. Ask for a
quote, then redeem it. Quoting is free, needs no key, and generates nothing:

    curl -X POST https://stargazer.vector.garden/v1/quotes -H "Content-Type: application/json" -d '{"title": "What the handbook says", "sources": ["https://example.com/handbook.pdf"], "shape": "briefing", "target_minutes": 6}'

Read the answer rather than this page. It carries the price, the job it covers,
when it expires, and the exact call that spends it, url already composed. Note
the spelling: a quote takes target_minutes, and the direct submit above takes
targetMinutes. Both are real; neither is a typo.

Redeem it with your key and a credit funds it. Redeem it with no key and the
answer is 402 with x402 payment requirements in the PAYMENT-REQUIRED header;
sign an authorization for the quoted amount and send the same request again
with it on PAYMENT-SIGNATURE.

    curl -X POST "https://stargazer.vector.garden/v1/episodes?quote=QUOTE_ID" -H "Authorization: Bearer sgk_your_key_here"

The body is optional on a redeem. Leave it out and the quoted job runs as
quoted. Send one and it must describe the same job, or the answer is
E_QUOTE_DESCRIPTOR_MISMATCH. A quote is single use: a second redemption is
E_QUOTE_CONSUMED and one left too long is E_QUOTE_EXPIRED. Both mean ask for a
new one rather than retry.

Money moves only after the episode is generated and passes verification, so a
run that fails costs the buyer nothing.

## Everything else

List the episodes this key can see, newest first:

    curl https://stargazer.vector.garden/v1/episodes -H "Authorization: Bearer sgk_your_key_here"

HEAD works wherever GET does, so you can check an episode or its audio exists
without pulling the file.

The whole api is described at https://stargazer.vector.garden/openapi.json as OpenAPI 3.1, and
https://stargazer.vector.garden/llms.txt is a one-screen version of this file.
