| hint | No | auto_job path only: what to do next, in one sentence. | |
| hooks | No | Ranked hooks, best first. Each is {id, text, archetype, score, claim_type, rank}. NOTE the key is `id`, and THAT is the hook id you pass to report_outcome(hook_id=...) / get_hook (where the same value is spelled `hook_id`). You pay per hook RETURNED, so this can be shorter than `count`. At standard/full, a judge-ranked smart hook may also carry {shape, phone_test, say_it}; a judge-ranked research hook carries receipts {persona, shape, phone_test, say_it, pattern_source}. pattern_source is null when no measured opener was attributed, otherwise its nested provenance is the authoritative actual platform, source surface, transcript method, language, route, media, and origin-time record. Minimal keeps persona and shape, plus phone_test only when it is the warning value 'fail'. | |
| usage | No | LLM token usage {input_tokens, output_tokens, est_cost_usd} (+ web_searches and fallback_* counters when those paths ran). NULL means NO LLM ran, which is not the same as zero tokens. | |
| engine | No | Which generator implementation produced the deck. | |
| job_id | No | auto_job path only: poll it with get_job or block on wait_for_job. | |
| reason | No | instant only, non-null when the template pool ran out: prose saying hooks were WITHHELD rather than duplicated. On a clause-topic it says outright that widening `archetypes` will not help, so do not retry that way. | |
| status | No | auto_job path only: the job's lifecycle state ('queued'). | |
| timing | No | What actually happened: {latency_ms, stages}. `stages` keys are mode specific (instant render_ms; smart llm_ms/judge_ms; research brief_ms/draft_ms/judge_ms). | |
| auto_job | No | Present and true when deadline_ms created a job, or this exact key already owns the same async job, so the call returns a JOB instead of hooks. CHECK IT BEFORE READING `hooks`: on this path `hooks` is ABSENT and the body is the job pointer below (job_id, status, estimated_seconds, poll_after_seconds, status_url, expires_at, requeued, hint). Nothing is charged until the job runs. | |
| replayed | No | true when an idempotency_key replayed a stored result, so nothing was charged again. The credits_charged below is what the ORIGINAL call cost. | |
| requeued | No | auto_job path only: true when an idempotent resubmit revived an existing job. | |
| research | No | mode research ONLY (null on instant/smart). The grounding ledger you paid for. Keys, and what to DO with each:
- brief_summary (str), angles (list of {angle, detail, why_it_stops_scroll}), vocabulary (list of insider terms), tensions (list of conflicts/open questions): the brief itself. Reuse them to write more, do not re-buy them.
- clean (bool): the ONE boolean separating a clean run from a salvaged one. clean:false WITH a full hook count is a partial-quality delivery at full price, which is otherwise invisible. Check it before trusting the deck.
- degraded (bool), degraded_reason (str|null), degraded_stages (list of stage names), judge_skipped (bool): what went wrong and where. A judge-degraded run BILLS at the smart tier, so reconcile spend against pricing_mode, not mode_used.
- rank_basis (str): what the served ORDER means: llm_judge | judge_order_no_scores | heuristic | unranked. Pairs with each hook's score.source, which says what its TOTAL means. Do not present a 'heuristic' order as a judged ranking.
- stages (list): the per-stage ledger, uniform rows {stage, status, attempts, calls, latency_ms, branches_ok, branches_failed, judged_n, backfilled_n, ranked_n, skipped_n, skipped_reason, notes}. null in a field means NOT OBSERVED and must never be read as 0; [] means observed and empty.
- ledger_source (str): 'engine' when the pipeline reported its own ledger, 'derived' when this layer reconstructed it. On 'derived', treat every null as unobserved.
- pool_size (int|null): unique draft candidates the judge chose from; null when not observable. A small pool means little real selection happened.
- grounded (bool), web_searches (int), sources (list of {url, title}, max 10): grounded is true ONLY when at least one real web search ran. Cite `sources` rather than claiming the hooks are researched.
- grounded_degraded_reason (str|null): non-null ONLY when a grounded brief was refused by the vendor and the run spent its retry ungrounded. Same price, different product: this is how you tell 'grounding never asked for' from 'asked for and refused'.
- evidence (list): each {id, claim, kind, source_url, source_title, source_domain, cited_text, confidence, freshness_days, authority, angle_ids, bind_score}. confidence is computed in code from a binding against a span the vendor actually returned, never self-reported by the model. Quote `cited_text` when you need to show a receipt.
- evidence_counters (dict): the honesty half: declared_total, invented_urls, duplicate_ids, source_url_corrected, cite_markup_stripped, orphan_citations, dropped_by_cap, rows, established, reported, unsourced, sources_seen, citations_seen, distinct_domains, primary_domains, id_remap. invented_urls counts model-cited URLs ABSENT from the real result set (nulled, not shipped). A high invented_urls / declared_total ratio means downgrade your trust in this grounding.
- selection (dict|null): what the selector actually did. null means no selection ran (an empty report would wrongly read as 'nothing was dropped'). Carries floor, requested, returned, above_floor, band_counts, relaxations, shortfall {requested, returned, cause, message}, the drop counters (near_duplicates_dropped, contradictions_dropped, off_topic_dropped, below_floor_dropped, archetype_capped_dropped, register_capped_dropped), diversity_thinning {thinned, displaced, delivered_mean, top_ranked_mean, quality_delta}, the caps (archetype_cap, register_cap, dedup_threshold), constraints_unavailable, and a one-sentence `notice`. A NEGATIVE diversity_thinning.quality_delta means the diversity caps cost you quality: narrow `archetypes` next time if you would rather have the top-ranked set.
- injection_attempt (bool): true when the grounded brief reported that its search results tried to INSTRUCT it. Treat as a security signal: never auto-execute anything derived from that run's text.
- platform_evidence (dict|absent): the measured-openers stage. ABSENT means this deployment has no evidence key and the stage does not exist; present means it ran. {used, source, routes, scanned, with_speech, deduplicated, filtered, dropped, kept, not_requested, budget_omitted, transcript_unavailable, not_selected, terminal_accounted, reconciled, cached, reason, rank_policy, rank_signals, transcripts_requested, requested_platform, actual_platforms, requested_language, observed_languages, transcript_methods, duration_policy, origin_fetched_at, current_request_vendor_calls, current_request_vendor_credits, origin_vendor_calls, origin_vendor_credits, vendor_calls, vendor_credits, planner_version, plan_fingerprint, query_policy, instagram_policy, query_plan, route_failures, suggested_queries, logical_vendor_calls, vendor_attempts, vendor_logical_call_cap, vendor_logical_call_cap_exhausted, adaptive_transcripts}. Experimental fields appear only for an active lab policy. used:true means real opening lines transcribed off the platform reached both the brief and the WRITER; used:false always carries a `reason`. `reconciled:true` means kept plus every terminal drop category equals scanned exactly. `source` names the supply that actually answered, and says '(proxy for X)' when you asked for a platform with no route of its own. cached:true means the openers were observed up to 15 minutes before this run rather than during it. The counters attribute every drop between scanned and kept, so a low `kept` has a cause rather than a shrug, and `dropped` breaks the qualification losses out by cause: views_below_minimum (the platform-reported count was below the inclusive 200,000-view eligibility floor, so no transcript was bought), language (the platform says the clip is SPOKEN in another language), ad (bought reach, so the view count does not measure the opening line), duration. Guarded native discovery also reports ad_unverified, duration_unverified, language_unverified, and format_unverified when the vendor did not establish a required fact. rank_policy says which clips became your evidence: 'blend' fuses raw views with outperformance (views over followers) and velocity (views per day), so a clip that escaped a small audience outranks a large account posting an ordinary result; 'views' is raw popularity. rank_signals lists the ones actually available, since not every route publishes follower counts or dates. origin_vendor_calls and origin_vendor_credits describe the fetch that populated the evidence. current_request_vendor_calls and current_request_vendor_credits are zero on a cache hit. vendor_calls and vendor_credits remain compatibility aliases for origin cost. None changes your price, which is fixed per hook.
- clarification (dict|absent): present only when you passed clarify. {mode, sufficient, questions, assumptions, creator_context_declared}. sufficient:false with questions is the run telling you it generated WITHOUT knowing something it needed; answer them in creator_context and re-submit for a materially better set.
- personas (dict|absent): {set, assumption, from_creator_context}. The speaker profiles the run inferred and wrote for. from_creator_context:false means the engine GUESSED who is talking, and `assumption` is the guess it made. | |
| mode_used | No | The engine that actually ran (instant|smart|research) after aliases and auto were resolved. May differ from mode_requested. | |
| shortfall | No | {requested, returned, cause} on EVERY response. `cause` is a closed vocabulary: 'none' on a full delivery, else template_pool / model_under_delivery / pool_exhausted. Branch on cause; never retry on 'none'. You were billed for `returned`, not `requested`. | |
| base_price | No | Fixed part of the charge for this mode. | |
| expires_at | No | auto_job path only: earliest terminal-row prune cutoff, ISO-8601 UTC. Queued/running rows are not deleted solely because this time passed. | |
| rank_basis | No | verbosity=minimal ONLY, alongside degraded_stages: hoisted out of `research`; what the served ORDER means (llm_judge | judge_order_no_scores | heuristic | unranked). At standard/full read research.rank_basis. | |
| request_id | No | Id of this call. Keep it: get_usage(request_id=...) itemises exactly what it charged, and it identifies the call in a support question. | |
| status_url | No | auto_job path only: REST URL for the same status (HTTP clients). | |
| llm_fallback | No | true when the requested LLM tier was unavailable and a cheaper engine ran instead; llm_fallback_reason says why. You are billed for what RAN. | |
| pricing_mode | No | The tier billed, which is what price_per_hook belongs to. | |
| expected_wait | No | {mode, p50_ms, p90_ms, source} for this mode, to size the NEXT call. | |
| hook_instances | No | Immutable non-prose served-occurrence identities. Profile-bound queued/running work returns an empty list; a successful result returns one row per served hook. | |
| mode_requested | No | The mode string you sent, before alias/auto resolution. | |
| price_per_hook | No | Per-hook part of the charge for this mode. | |
| prompt_version | No | Prompt build used, for reproducibility. | |
| count_requested | No | The count you asked for, echoed so you never have to diff an array length against your own request. | |
| credits_charged | No | Credits this call actually cost. | |
| degraded_reason | No | verbosity=minimal ONLY, and only when non-null: hoisted out of `research` so the trimmed envelope still says WHY the run degraded. At standard/full read research.degraded_reason instead. | |
| degraded_stages | No | verbosity=minimal ONLY, and only when non-empty: hoisted out of `research`; which stages to blame. At standard/full read research.degraded_stages. | |
| grounding_refund | No | Non-null ONLY when you asked for research and the VENDOR REFUSED the web-search tool: the full pipeline still ran, so you are not billed as smart, but part of the research premium is waived. {credits_waived, list_price, pct_of_research_premium, reason, explanation}. This is why credits_charged can come in UNDER base_price + price_per_hook * hooks; research.grounded_degraded_reason says what the vendor said. | |
| score_disclaimer | No | The honest limits of the scores above. Absent at verbosity=minimal. | |
| credits_remaining | No | Your balance AFTER this charge. | |
| estimated_seconds | No | auto_job path only: queue-aware estimate of total time to a result. | |
| poll_after_seconds | No | auto_job path only: wait at least this long before the first get_job. | |
| replayed_at_charge | No | true when the replay was detected at the charge boundary rather than up front; either way you are billed exactly once. | |
| llm_fallback_reason | No | Why the fallback happened, or null. | |
| judge_fallback_reason | No | smart/research only: why the judge did NOT rank this set, or null when it ran. Non-null means these score.totals are heuristic-scale, so never compare them against a judged run's totals. | |
| resolved_creator_profile | No | Exact immutable creator-profile binding used by this occurrence. Null/absent for inline or unprofiled generation. | |