Niche
Editorial intelligence for creators and their agents.
What it can do
- Niche Signal Scan: Niche (nicheangle.com) story discovery: find stories worth writing about, then draft and publish platform-native social content (LinkedIn, X threads, Instagram, newsletter) from the
- Niche Draft Direct: Draft the creator's OWN take or product straight into posts — NO research scan, no story/angle picks. Use this for product-led / bring-your-own-content work: a specific thing to sa
- Niche Intelligence Query: Niche (nicheangle.com) research and analysis: answer an ANALYST-shaped question over fresh-scanned sources and get an intelligence answer as the deliverable — not a single po
What data it sees
Do you need an account
No: the server works without sign-in
Editorial intelligence for creators and their agents. Most tools help you write, Niche helps you decide what's worth writing. It reads primary-source signal, discovers emerging stories, ranks the strongest angle, and turns that signal into publishable, platform-native content that's grounded in real sources, in your voice.
25 tools across discovery, drafting, render, and brand. Remote (Streamable HTTP), OAuth 2.1 + PAT.
How to connect
How to connect Niche to Claude
The server address to paste into Claude settings:
https://niche--sailquery.run.tools
- Open Claude (claude.ai or the desktop app).
- Go to Settings → Connectors.
- Click Add custom connector.
- Paste the server address copied below into Remote MCP server URL and click Add.
- In a chat, click + → Connectors and switch the new connector on.
Custom connectors are available on Free, Pro, Max, Team and Enterprise plans (Free is limited to one). On Team and Enterprise an organization Owner adds the connector first under Organization settings → Connectors.
Authorization
After you click Add, a sign-in window for the service opens. Sign in with your own account and approve access. Claude never sees your password.
How to connect Niche to ChatGPT
The server address to paste into ChatGPT settings:
https://niche--sailquery.run.tools
- Open ChatGPT in a browser (chatgpt.com). A Plus, Pro, Business, Enterprise or Edu plan is required.
- Turn on developer mode once: Settings → Apps → Advanced settings → Developer mode.
- Open Settings → Connectors and click Create.
- Fill in the form: Name (anything), Description (one line about what the service does), MCP server URL (copy it below).
- Under Authentication choose OAuth if the service requires sign-in, otherwise None. Click Create.
- In a new chat open + → Apps/Connectors and enable the connector.
OpenAI has renamed this section before (Connectors → Apps/Plugins). If the label differs, search settings for "developer mode". On Business/Enterprise workspaces an admin must allow custom connectors first.
Authorization
On first use ChatGPT opens the service's sign-in window. Sign in and approve access.
How to connect Niche to Cursor
The server address to paste into Cursor settings:
https://niche--sailquery.run.tools
Fastest: click Open in Cursor below and confirm the prompt.
Manually:
- In Cursor open Settings → Cursor Settings → MCP and click Add new global MCP server.
- Paste the JSON copied below into
~/.cursor/mcp.json(per project:.cursor/mcp.jsonin the repo root). - Save the file. The server appears in the MCP list; authorize it there if asked.
Authorization
If the service needs sign-in, an authorize button appears next to the server in the MCP list.
Server tool list (25)
Raw names from tools/list. Only developers need these.
| niche_signal_scan | Niche (nicheangle.com) story discovery: find stories worth writing about, then draft and publish platform-native social content (LinkedIn, X threads, Instagram, newsletter) from them. This is story discovery, not content generation: Niche reads primary sources, separates signal from noise, and clusters it into a ranked story slate with provenance — the editorial-intelligence step before any writing. Returns a session_id + initial status; poll niche_session_state with the session_id until status is `cp1_awaiting_story` to read the slate. BRAND PROFILE: pass `brand_id` to bind this run to a persisted brand profile (set via niche_brand_profile_set). The profile's voice, lexicon, framing, channel config, and verifier overrides thread through every downstream stage. Pass `profile_overrides` alongside `brand_id` to deep-merge a one-time deviation onto the persisted profile (logged on the session, not stored). The effective profile is snapshotted at scan time — subsequent updates to the persisted profile don't affect in-flight runs. |
| niche_draft_direct | Draft the creator's OWN take or product straight into posts — NO research scan, no story/angle picks. Use this for product-led / bring-your-own-content work: a specific thing to say ('new walnut dining table, live edge, $2,800') or a page to repurpose. The signal pipeline (niche_signal_scan) is for 'what's worth writing about my niche'; THIS is for 'write this exact thing.' Provide AT LEAST ONE OF: `take` (what to say) / `source_url` / `source_text` (the last two repurpose an existing page). Async: returns a session_id in <2s; poll niche_session_state(wait:30, wait_until:'checkpoint') to cp3_awaiting_review/complete, then read outputs[]. Pass `brand_id` to bind the creator's voice + offer + CTA so the draft sounds like them and pitches their wares (set those first via niche_brand_profile_set). |
| niche_intelligence_query | Niche (nicheangle.com) research and analysis: answer an ANALYST-shaped question over fresh-scanned sources and get an intelligence answer as the deliverable — not a single post. Use for: 'the 10 biggest developments in X this week', 'what's emerging before it goes mainstream', 'where is investment activity rising', 'find 3 non-obvious narratives to publish on LinkedIn'. The answer is the ranked slate PLUS engine-GROUNDED narratives or patterns: every narrative cites real slate stories and is fact-verified by a second pass (no source → no narrative). This uses the same engine as the Niche web app, so both surfaces return the identical grounded answer — do NOT synthesize your own narratives over the slate; present these. NON-BLOCKING: returns a `session_id` immediately (<2s). Poll niche_session_state every ~3-5s. At status == cp1_awaiting_story the ranked slate (`stories[]`) is ready — present it right away. Synthesis fills in a few seconds AFTER the slate: if you requested it, keep polling while `synthesis_pending == true`; once false, `synthesis[]` holds the grounded narratives (or `synthesis_shortfall_note`). With synthesis:'none', `synthesis` stays null and `synthesis_pending` is false at cp1 — stop there. To turn a narrative into a post, pick its supporting story id and call niche_angle_propose — no new scan needed. Pass brand_id to thread a brand profile. |
| niche_session_state | Universal poll endpoint — read the FULL current state of a session. Returns status, ranked stories, picked story_id, generated angles, picked angle_id, draft outputs (with trust fields), and an elicitation hint for whatever decision is next. Call this whenever you need to check progress; it's safe and cheap. Each story includes title, summary, headline_candidate (the post-shaped headline distinct from the cluster title), recency_score, relevance_score, freshness_label, and the publication_breakdown of contributing outlets (provenance). Each story also carries a recommended_story_id + recommendation_reason before a pick. Each draft output's trust data lives under `outputs[i].trust.*` (verifier_blocked_reason, source_faithfulness_score, source_ungrounded_claims, source_diversity_passed, source_recency_passed, source_distinct_count, plus a flags[] array with explicit severity + source_grounding_map). The output top level does NOT mirror these — read them from `.trust`. Response also includes `phase` (high-level: scanning / drafting / filed / spiked / awaiting), `phase_message` (rotating gerund — Reading 337 signals…), `phase_hint` (one-line agent-facing tooltip with a typical timing band — Clustering — usually 8-15s, no action needed). The full 17-status state machine is enumerated under `status_glossary` so you can introspect what every state means without discovering it experimentally. For a TERMINAL run, read `outcome` (complete / expired / interrupted / cancelled / failed) rather than the raw `status` — a `failed` status is usually an expired walk-away (a slate was produced) or a refunded interruption, not a real error. BEST LOOP (do this): kick off work, then ONE niche_session_state(wait:30, wait_until:'checkpoint') per stage — it sleeps through the noisy transient statuses (clustering, ranking, generating_*) and wakes ONLY at the next actionable stop (cpN_awaiting_* / complete / failed), or when an async render settles. So a full run is one wait per checkpoint, not 2-3 wakes per stage. (`wait_for` is an accepted alias for `wait_until`.) The older `wait` + `since_status` long-poll (wait_until:'change', wakes on ANY status change) still works for back-compat, but prefer 'checkpoint'. Each status' `actionable` flag in `status_glossary[]` tells you which states a 'checkpoint' wait wakes for. Do NOT poll every few seconds without `wait` — that's what trips a 429. Alias: niche_story_search (legacy name, supported for back-compat — the new name reflects the actual job). Response shape is sparse by default: after a story is picked, only the picked story is returned (not all candidates); same for angles. Set include_unpicked=true to get the full candidate set — useful when revising to a different story or angle. A `sparse_mode` field in the response reports how many items were dropped. |
| niche_angle_propose | Niche content angles: pick a story from the discovery slate and surface the strongest angles worth publishing — the editorial-judgment step that turns a development into a piece. Returns five angles[], each with frame, hook, tension, cta_direction, and cta_variants (swap palette). niche_session_state then carries an angle_recommendation (recommended_angle_id + reasoning); when a brand profile is bound it is brand-fit-scored, otherwise recommended_angle_id is null with recommendation_basis='default_ordering' (no fake pick). Returns immediately with status=cp2_generating; poll niche_session_state until angles[] is populated. CUSTOM FRAMING (provenance-preserving): to draft YOUR OWN angle on this researched story — not one of the proposed five — pass `custom_framing` (after the story is locked + angles are ready). The framing is shaped onto the REAL story and drafted on THIS session, so the trust block keeps the story's actual sources. Use this instead of niche_draft_direct when you have a researched story in hand — draft_direct seeds a synthetic 1-source session and drops the provenance. |
| niche_draft_create | Niche draft content: lock the chosen angle and turn it into platform-native social drafts — LinkedIn post, X thread, Instagram, newsletter/long-form — ready to review and publish (the downstream payoff once the story + angle are decided). Returns immediately with status=cp3_generating; poll niche_session_state until outputs[] is populated. Each output carries its trust data NESTED under `outputs[i].trust` — `source_faithfulness_score`, `source_ungrounded_claims[]`, `source_diversity_passed`, `source_recency_passed`, `overall_severity`, a severity-tagged `flags[]`, and `verifier_blocked_reason`/`verifier_blocked_claim` when a fabrication was refused. (No top-level `verifier_audit` field — read `outputs[i].trust`.) |
| niche_draft_revise | YOU write the new caption/fields and pass them in — this does NOT rewrite from an instruction (unlike a chat 'make it punchier'). To change the copy, supply the new copy as `caption`. Applies the values you pass to a specific output. Accepts any subset of the output's fields — caption, hashtags, or partial script updates (hook / body / cta / hook_tweet / body_tweets / title / subtitle / pull_quote / cover_slide / slides / cta_slide / alt_text). Pass `apply_hook_variant_index` to splice an existing hook_variants[N] into the live hook in one move without rewriting the rest. If you pass no editable field (or values identical to the current draft) it changes nothing and returns `status:'no_change'` naming the params that edit content. Angle / story changes still go back through niche_angle_propose — they invalidate the verifier trust block and need fresh generation. Response includes a `diff[]` array listing every field that changed (`{field, before, after}`) so agents can show users the delta rather than the full new payload. |
| niche_draft_publish | Publish a single output to its platform. Defaults to dry_run=true — returns the would-publish payload + any verifier blocks WITHOUT actually filing. Set dry_run=false AND provide an idempotency_key to commit. The commit is the ONLY irreversible action in the workflow; the agent should present the dry-run preview to the human and only commit on explicit go-ahead. Verifier-blocked outputs refuse to publish even with dry_run=false; clear the block on the row first. When the target social isn't linked, returns status='not_connected' + a `connect_url` instead of publishing — send the user to connect once, then retry. |
| niche_brand_profile_set | Set (or update) the persisted brand profile for a brand the user manages. The profile is a structured JSON document that's load-bearing across every pipeline stage — voice rules, banned terms, canonical vocabulary, framing allowlist, channel config, compliance disclosures, verifier overrides. Use this when: - The agent has derived a brand profile from a repo / docs and wants to persist it so future runs inherit the rules. - An update to voice rules / banned terms needs to land before the next run. REQUIRED sections: `identity` and `voice` (a profile with no voice is rejected — it's what drives the generic-fallback drafts). A profile saved before this requirement is grandfathered: a re-set that omits voice gets a default voice stub auto-added rather than rejected, so legacy profiles aren't stranded. Validation: tiered lints (error / warn / info). `error` lints REFUSE the set entirely. `warn` lints accept-with-note. The response always includes `lints[]` so the caller sees ambient warnings. `conflicts[]` surfaces BrandKit-locked field collisions — the BrandKit wins on those paths and the profile values are dropped from storage. v1 ships persisted scope only; session and inline_overrides scopes are per-call constructs (passed alongside other tool arguments). brand_id is unique-per-user — re-setting the same brand_id replaces the prior profile. |
| niche_list_sessions | Enumerate the user's recent sessions. Returns id, niche_input, status, `outcome`, target_platforms, picked story/angle ids, and created/updated_at for each. Use this when the session_id has been lost (across agent invocations, hours of work, etc.) or to find an in-flight session to resume. Returns newest-first. Judge a terminal run by `outcome`, NOT raw `status`: a `failed` status is usually a walk-away, not an error. outcome ∈ {complete, expired (a slate was produced but nobody picked — re-open + choose), interrupted (a restart ended it; credits refunded — just re-run), cancelled (stopped on purpose), failed (a real error — see error_message), running}. (`status_filter` still matches the raw status value.) |
| niche_brand_kit_ingest | Auto-populate the user's BrandKit (palette / fonts / tagline / logo / wordmark / boilerplate / voice notes) from files OR a URL OR pasted text. By default ADDITIVE — fills empty fields, leaves populated ones alone. Idempotent: re-running the same inputs doesn't double-write. OVERWRITE RULE (important): if the target brand kit ALREADY has an identity (a tagline/boilerplate/voice for a DIFFERENT brand), do NOT silently overwrite it. First ASK the user whether to replace it. If the account supports multiple brand profiles, prefer creating a SEPARATE brand instead — pass a new `brand_id` slug + `brand_name` rather than clobbering the existing one. Only pass `replace=true` once the user has confirmed they want this brand re-learned from the new source. Use when the agent has brand assets in scope (a working directory with logos / press-kit / brand-guide PDFs, the user's portfolio or Substack URL, pasted boilerplate copy) and wants to populate Niche's BrandKit so future signal_scan + content_generation stages inherit the brand context. Agent-side equivalent of the web /brand-kit ingest surface — same backend engine. ASYNC + POLL — a URL or multi-file ingest runs in the background, so this call returns FAST with {ingest_id, status:'ingesting'}. Then poll niche_brand_kit_ingest_status(ingest_id) until status is 'done' — that response carries the populated BrandKit, the ingest report (detected[] / skipped[] / errors[]), and a diff[] of changed fields. (Loop: ingest → poll status until done/failed; same pattern as niche_signal_scan → niche_session_state.) Do NOT re-call ingest while one is running — a duplicate of the same inputs attaches to the in-flight job. URL ingest also fills voice primitives when the page has post-shaped text (Substack/blog/X). If a URL is slow or thin to scrape, the visual fields may land before the voice pass completes; when the report flags this, paste the page's About/homepage copy via `text=` to complete the brand voice. |
| niche_brand_kit_ingest_status | Poll the result of an async niche_brand_kit_ingest. Pass the `ingest_id` it returned. status: 'ingesting' (keep polling) | 'done' (response carries the full kit + detected[]/skipped[]/errors[]/diff[]/left_unchanged_because_populated[]) | 'failed' (error + failed_step; the kit was NOT written). The marker expires 30 min after the ingest starts. |
| niche_voice_profile_ingest | Extract voice primitives (register / sentence rhythm / lexicon preferences / punctuation habits) from post-shaped text and persist onto the user's VoiceProfile. The voice primitives thread into content_generation prompts so generated copy matches the user's actual writing voice. Two input shapes: pass `posts` (list of pre-collected text snippets, ≥80 chars each) or pass `url` (we scrape post-shaped snippets from the page — Substack / Medium / blog / X profile). Inline posts win when both are given. Inline post-shaped snippets need to be the user's OWN writing — not press articles or marketing copy. Returns the extracted primitives + a diff of what changed on the stored VoiceProfile. |
| niche_brand_profile_get | Read-back tool: verify what's actually persisted for a brand profile WITHOUT re-setting it. Use after niche_brand_profile_set to confirm the schema landed as expected; use mid-session to double-check the active profile a run is bound to; use before niche_brand_profile_set when an agent is about to write a partial update and wants to inspect the current shape first. Pass `brand_id` to read one profile; omit it to list all (brand_id, updated_at) summaries. Returns the full profile JSON + schema_version + created/updated timestamps + a brand_kit_sync_status hint reflecting whether the profile's mirrored fields are in lock-step with BrandKit. Lets an agent introspect the active profile shape instead of inferring it from prior set calls. |
| niche_brand_kit_guided_setup | Return a structured question chain the agent walks the user through to populate the BrandKit + VoiceProfile + (optionally) BrandProfile. Each entry carries an `intent` describing what the answer is FOR (so the agent paraphrases in its own voice based on the conversation it's already having) plus a `prompt_hint` fallback for agents that relay tools verbatim. Use this when the agent is helping a new user set up Niche and wants a predictable, brand-aware Q&A sequence instead of improvising. Tiered by impact: - Tier 0 (primary): URL ingest — fills 70-90% in one ask. - Tier 1 (gap_fill): only the fields URL ingest didn't fill. - Tier 2 (discipline): opt-in guardrails (topics off, banned terms, competitor stance). - Tier 3 (no_url): full fallback for users without a site. Per-question `applies_to_field` tells the agent where the answer writes (via niche_brand_kit_update or niche_brand_profile_set). `is_already_set` is computed from the user's current BrandKit + BrandProfile state so the agent skips questions already answered. Returns the FULL chain in one call — the agent inspects state, decides flow, and asks in any order (or skips entirely if the user volunteered the answer earlier in the conversation). |
| niche_brand_kit_update | Set specific BrandKit fields by name. The write path for the structured fields (tagline / boilerplate / voice_notes / forbidden_phrases / signature_phrases / endcard preferences / video voice preference / colors / fonts) without going through the ingest engine. Use after niche_brand_kit_ingest fills the easy stuff, or to commit values the user answered through niche_brand_kit_guided_setup. Only fields you pass are touched — fields you omit stay at their current value. Lists REPLACE the current value (not append). Response includes a diff[] of fields that changed and the full updated kit. |
| niche_render_image_card | Render a visual onto a CP3 post — or, with scope='recomposite', EDIT THE TEXT on an existing image for FREE (same background, new words/color/size — NEVER re-render just to change text, that costs ~30cr and swaps the art under the user). scope='restore' brings back the prior image from history, also free. House rule: presentation changes never route through generation. For a NEW render (scope='full', the default) you MUST pick `background` — there is NO default, on purpose: the two modes differ sharply in cost + result, and a wrong guess either burns credits or returns a flat color when you wanted a picture. • background='photo' — a GENERATED AI/photographic image made for the story, with your headline composited over it. This is the actual picture. The output's script keeps `bg_url` — the CLEAN generated background retained separately from the composited card; it powers FREE text/color/size re-composites + restore (no regen). ~30 credits, ~30-90s, ASYNC: returns status='rendering_image_card'; poll niche_session_state (`image_render.status`: rendering → done, static_urls land on the output / failed, credits refunded). Don't re-call on a perceived hang (a second call no-ops while one is in-flight). • background='brand_color' — a FREE, instant flat BRAND CARD: your headline on the brand's solid color with logo + wordmark. No image generation, no credits, milliseconds. SYNCHRONOUS: the card is in outputs[] right away. `headline` sets the bold header for either mode (defaults to the post's card_headline; auto-fits, never truncated). Idempotent: a prior render is replaced. Errors: render_not_ready if pre-CP3; render_not_configured (photo) if no image-gen key; render_card_unavailable if brand_color but the piece has no card-bearing platform. |
| niche_render_reel | Render a 9:16 vertical reel for a session that's reached CP3: a per-beat script (typically 4-7 beats) becomes stills + voiceover + motion + caption overlays + an endcard, composited into one video (~30-120s). scope='recomposite' re-renders an EXISTING reel's presentation for FREE from its kept footage — captions on/off, caption_sync_mode, endcard words — same beats, same voiceover, 0 credits (house rule: presentation changes never route through generation). NEVER pay a full re-render just to flip captions or fix the endcard line. scope='endcard' is the same free door narrowed to the endcard only; scope='beat:N' re-rolls ONE shot's image (same script, fresh take, optional beat_direction steer) for a small honest charge and reuses everything else. HEADS-UP before spending: reels are DOWNLOAD-ONLY for now (direct Instagram publishing is pending platform approval) — the user posts the file themselves. Set that expectation before the render. Idempotent on the video platform: any prior render is replaced. Toggles: `captions_enabled` (default true) overlays the per-beat caption text on each still; `music_enabled` (default false) mixes background music behind the voiceover; `tone` shapes the script — `default` | `punchy` | `direct` | `reflective`; `caption_sync_mode` (default 'phrase' — captions track the voiceover in lockstep) — 'phrase' reveals captions phrase-by-phrase in lockstep with the spoken words (text taken from the VO). Phrase mode adds NO extra credits (it reuses timestamps already fetched). Ignored when captions_enabled is false. Errors: 503 if the render backend isn't configured; 400 if pre-CP3; 422 if the script came back unusable. Cost: standard tier = ~350 credits (one still per non-endcard beat + voiceover + composite). Premium opt-in is a separate path. |
| niche_session_revert | Revert a session back to an earlier checkpoint. Use when the user (or you) decided the picked story / angle isn't the right one and you want to re-pick without starting a new scan. to='story' → cancels current generation, returns to CP1_AWAITING_STORY with the same ranked stories list. Clears selected_story_id + selected_angle_id. to='angle' → returns to CP2_AWAITING_ANGLE with the same angles list. Clears selected_angle_id only. Idempotent: reverting a session already at the target checkpoint returns the current state unchanged. SAFETY: reverting a finished run (cp3/complete) discards its paid-for drafts. The first call returns status='confirm_discard' with the count instead of reverting; pass acknowledge_discard=true to actually proceed. Credits: the discarded drafts are NOT refunded (the generation already ran), and re-picking re-runs + re-charges content generation — revert to change direction, not to reclaim spend. |
| niche_session_cancel | Cancel a running session. Marks status=failed with an error_message ('cancelled by user' or the caller's reason), and stops the in-flight orchestrator if any. Used when you want to free a slot under the concurrent-session cap WITHOUT deleting the session (history + audit trail preserved). Use the REST DELETE endpoint if you want a hard archive cleanup that also wipes outputs. Idempotent: cancelling a completed / already-failed session returns its current state unchanged. Credits: the UNUSED hold is released; work already committed (e.g. the discovery for a story you picked) stays charged — cancelling does not refund committed work. A pre-CP1 cancel (nothing committed yet) is effectively free. A full refund is reserved for an actual run FAILURE. |
| niche_session_export | Export a session as a structured calendar artifact preserving session_id + per-story story_id traceability. Use after a niche_signal_scan when you want a METADATA-RICH CONTENT BACKLOG instead of running individual pieces end-to-end. Outputs a standard editorial-calendar shape suitable for content-backlog and planning workflows. Two formats: • markdown — human-readable + agent-citable. Session metadata at top (session_id + niche + scan timestamp + brand_profile_active state). Then a card per story with title, headline_candidate, summary, recency_score, publication_breakdown, source_breakdown, and EMPTY slots for the user to fill (Frame, Hook, Article-shape, Ship Order). Followed by a 'recommended ship order' section and cross-cutting notes. • json — structured shape ready to pipe to other tools or load into a notebook. Same data, machine-shaped. Preserves story_id + session_id traceability so you can come back in N weeks and re-run niche_angle_propose / niche_draft_create against the same stories with the same brand profile bound. The artifact is the entry point to a calendar-builder workflow. |
| niche_add_output | Add an output cell to a session that's already reached CP3. Use this when the user picked a small initial cell set, previewed the drafts, and now wants another surface — e.g. session started with linkedin:text_post, user wants to add instagram:carousel too. Text-only cells (linkedin:text_post, x:thread, long_form_article, etc.): generates text via the matching generator if it hasn't run yet, then creates the Output row. This call BLOCKS synchronously ~20-30s when it must run a new generator family (no status to poll); it returns fast when that family already generated. Idempotent — if the cell is already on the session, returns the existing Output unchanged. Asset cells (linkedin:image_post, x:reel, instagram:image_post, etc.): creates the text Output row; image-bearing cells AUTO-ATTACH a free branded card (static_urls populated without an explicit render — swap it anytime), while video/reel assets are never auto-rendered. The response's next_step says exactly what to call for more: niche_render_image_card (cell + an explicit background: 'photo' or 'brand_color') or niche_render_reel (cell). The response's copy_lineage says whether this cell drafted FRESH text or shares its generator family's existing copy. Errors: 400 if session is pre-CP3; 422 if cell is invalid. |
| niche_reuse_asset | Copy an image that ALREADY exists on one output onto another cell — instant + FREE (no regen, no credits). Use this when the user wants 'the same image' on a second surface ('use the LinkedIn image on X', 'same picture on the newsletter') instead of niche_render_image_card (which generates a NEW image and costs credits). Both cells must already exist on the session (add the target via niche_add_output first if needed) and the source must have a rendered image. Copies the source's static_urls onto the target so it publishes with that image. Idempotent: source==target is a no-op. |
| niche_attach_image | Attach a USER-SUPPLIED photo to a post — the creator's OWN image (a product shot, their actual work) as the post's visual, instead of an AI-generated image (niche_render_image_card photo, paid) or a flat brand card. FREE — no image-gen spend. For a visual-product maker the real piece IS the sale. Pass the cell + the image as base64 ({mime_type, data_base64}). The cell's output must already exist (use niche_add_output first if needed). Sets it as the post's image; publishes with the caption. Undo-able (the prior image is kept in history). |
| niche_whoami | One safe read-only call that orients an agent: WHO it is (the authenticated account, plan, and credit balance), WHAT the server is (version, endpoint, transport, live tool count), the CAPABILITY MAP (every tool grouped into functional bands — discover / decide / draft / render / publish / brand / session — plus the recommended flow), and the BRAND STATE (whether a brand profile / kit / voice will shape output, so the agent knows BEFORE a run whether drafts will read personalized or as neutral commentary). Call this first to plan a session and to check credits before spending. |