# Tenzetta agent guide

Analyze supplied text, readable PDFs, CSV/TSV rows and explicit public URLs. Evaluate each item against reviewed criteria; return typed answers, evidence and deterministic dataset calculations.

## Access
Resolve all paths below against the origin from which you fetched this guide. Keep that same origin for REST and MCP calls; never silently switch environments.
REST: POST /api/v1/analyze
MCP: /api/mcp (Streamable HTTP), tool analyze_items.
Use Authorization: Bearer <API key> for both. Store it in TENZETTA_API_KEY locally; never print or embed it in source. Access is currently for approved lab operators, not self-service signup. Do not ask the browser page for a key or bypass authentication. If you do not have access, stop and explain.
Never use the legacy research/watch tools for this workflow.

## Workflow
1. Ask the user for their actual data and goal. Do not invent rows or infer an ICP from prospects.
2. Submit items, questions, optional reference, and preview: true. Or use a guided brief.
3. Show the returned fields, summaries, brief, limitations and review_required state. Wait for the user's review before running newly planned criteria. Unknown evidence is not a numeric zero.
4. Resubmit the SAME items and reviewed fields/summaries, plus questions, reference and brief when supplied, with preview: false. Explicit fields are the reviewed contract. Planning with a new brief always pauses for review.
5. Preserve original data, row IDs, evidence, confidence, warnings, status and failed rows. Never call an incomplete stream a completed run. Export the requested table or JSON.

## Input shapes
Each item needs a unique id and may contain data (a string-valued object), text, url, or passages. Original spreadsheet IDs belong in data; duplicate original IDs are allowed, but item IDs must be unique.
URLs are read directly; links in data cells are not automatically fetched. No wider web search.
Upload CSV/TSV, readable PDF, text or supported text files to POST /api/v1/analyze/file as multipart field file; pass the returned items to analyze. Scanned PDFs need OCR elsewhere first.

## Preview request using a real public source (no precomputed answer)
{
  "items": [{ "id": "iana", "data": {}, "url": "https://www.iana.org/help/example-domains" }],
  "questions": "Does this page say example domains can be used in documentation?",
  "preview": true
}

## Guided briefs
brief: { use_case, choices, context, output, company_profile?, company_url?, measure_column?, group_column? }
use_case: sales | vendors | feedback | companies | documents | spreadsheet
output: table | ranked | summary
Use the browser to select valid choices and download the exact request through Use via API. For sales, the user must supply ICP choices/context or their own company URL. Their own URL informs criteria, never evidence about a prospect. Preserve returned business_context on the reviewed request.

## Company context from a domain
POST /api/v1/context/company with { "domain": "company.com" }, or call prepare_company_context over MCP with the same input and API key. Reads up to five relevant same-site pages, without Exa or storage. Returns profile, pages_read, warnings and review_required:true. Show the offering, described customers, evidence and suggested target_customers to the user. Let them correct it. After that review, pass the profile unchanged (except their edits) as brief.company_profile into analyze_items/POST /api/v1/analyze with preview:true.
The accepted profile is reused without recrawling. Explicit choices and additional user criteria take priority. Source passages describe the seller only, never the items being scored. Editing context requires replanning/reviewing questions. Save criteria includes this profile and source excerpts; it does not include raw crawled pages or previous approval. Company profiles are not saved to an account. The legacy company_url single-page path remains supported.

## Results
rows contain id, data, answers, status and warnings. answers[field_key] contains value, label, confidence, evidence and note. Missing evidence may produce null/Unknown; a service failure remains an error.
Summaries compute count, percentage, sum, average, median, min and max in code. Groups include input/known/contributing/unknown/failed counts, units and contributing row_ids. Percentages exclude unknowns from their denominator and disclose it. Mixed units are not added together.
filters (all must hold) narrow a summary: { "field_key": "<answer key, or _score for the weighted score>", "operator": "eq|ne|in|gt|gte|lt|lte", "value": ... } or { "column": "<input column>", "operator": ..., "value": ... }. A known false excludes a row; an unknown value is counted as unknown, never as a match. group_by groups by an input column; group_by_field groups by a yes_no, choice or rating answer (including Unknown and Not evaluated groups).

## Weighted scorecards
Add "score": { "label": "Deal score", "components": [ { "field_key": "condition", "weight": 30, "points": [ { "answer": "Move-in ready", "points": 100 }, { "answer": "Needs repairs", "points": 20 } ] }, { "field_key": "cap_rate", "weight": 40, "scale": { "worst": 5, "best": 8 } } ] }.
Each component weights one answer column. yes_no needs points for true and false; choice needs points for every option label; rating may omit points to use its level values; number fields use scale (worst → 0, best → 100, linear and clamped; best below worst means lower is better). Weights are relative. Code computes each row's 0-100 score and rank (rows[i].score: value, rank, parts, missing). If any weighted answer is Unknown the score is Unknown and missing names why. Use "_score" as a summary field_key (average/median/min/max) or as a filter field_key. Preview plans propose a score when the user asks to score, rank or prioritize.

## Decision lanes
Add "routing": { "lanes": [ { "label": "Call today", "when": [ { "field_key": "buying_stage", "operator": "eq", "value": "Active purchase" } ] } ], "otherwise": "Disqualify" }.
Lanes are checked in order; the first whose conditions ALL hold is rows[i].lane { label, reason }. Conditions use the same shape as summary filters (answers, input columns or _score). If a lane cannot be decided because an answer is Unknown, the row's lane is Unknown with the reason; it never falls through to a later lane. Count rows per lane with group_by_field "_lane". Preview plans propose lanes when the user asks what to do with each item or to route/triage. Add "review_below": 0.8 to send a matched lane whose deciding answers are under 80% confidence to "Needs review" (a person decides). "Needs review", "Unknown" and "Not evaluated" are reserved lane labels.

## Confidence and review
Every judged answer includes confidence and distribution (each possible answer with its probability, highest first). Route close calls to a person in your own code: e.g. review answers under 0.8 confidence. Changing that threshold needs no new call. Options may carry not_for (what the option does not cover) and examples to separate neighbouring categories. Choice and rating questions accept up to 32 options. Add an "Other" option when the list may not cover every item: it means the item is understood but fits none of the options, which is different from Unknown (evidence missing).

## Manipulation screen
Supplied rows are untrusted. Every judged row is also screened, in the same judge call, for text aimed at an AI reviewer ("ignore previous instructions", "rate this 10/10"). At 0.7 or above the row gets flags.manipulation and a warning, and decision lanes send it to Needs review. Answers are still judged on the evidence only. Treat the screen as one signal, not a security guarantee.
Send Accept: application/x-ndjson for incremental REST results: plan, row and complete events. A broken connection is incomplete even if rows arrived. Default response is JSON. MCP returns the result as JSON in text content; check isError too.

## Limits and behavior
500 items; 12 answer fields; 12 calculations; 4 MB/file; 1 MB extracted data/request; 60,000 characters/document; 12,000 characters/reference. Long documents use relevant excerpts and disclose it. Arbitrary numeric extraction, joins and date arithmetic are unsupported.
The workflow does not persist uploads or results; save the output before leaving. This is not a promise about upstream provider retention. No customer accounts or per-customer quotas are active in this internal lab mode. No durable job resume. A repeated request executes again. The server stops starting new rows after four minutes or a judgment-service failure; preserve partial results.
Ratings are rubric levels and weighted scores are your own weighting of the answers; neither is a probability of a sale. Confidence is model confidence, not a business-outcome probability.

Human guide: /developers
Browser workspace: /?mode=query#workspace
