Docs
Order and read studies from your agent or your own code
ProductTex is an MCP server over HTTP. Every tool is also a plain JSON endpoint. You need an API key from the start page.
Connect over MCP
The server speaks MCP over streamable HTTP at https://178-156-204-0.sslip.io/mcp, authenticated with your API key as a bearer token.
Claude Code
claude mcp add --transport http producttex https://178-156-204-0.sslip.io/mcp --header "Authorization: Bearer ptx_…"Cursor and other clients
Add this to your client's MCP configuration (for Cursor, ~/.cursor/mcp.json or .cursor/mcp.json in the project):
{
"mcpServers": {
"producttex": {
"url": "https://178-156-204-0.sslip.io/mcp",
"headers": { "Authorization": "Bearer ptx_…" }
}
}
}Treat the key like a password. Anyone holding it can spend your credits. Keep it out of committed files — most clients accept an environment variable in the header value — and revoke it from the dashboard if it leaks.
REST
Every tool is POST https://178-156-204-0.sslip.io/api/tools/<tool> with the tool's arguments as a JSON body. The response is the same JSON the MCP tool returns.
curl -s https://178-156-204-0.sslip.io/api/tools/list_offerings \
-H "Authorization: Bearer $PTX_KEY" \
-H "Content-Type: application/json" -d '{}'curl -s https://178-156-204-0.sslip.io/api/tools/design_study \
-H "Authorization: Bearer $PTX_KEY" \
-H "Content-Type: application/json" \
-d '{"url": "https://example.com/pricing",
"customer_description": "owners of small design agencies in the US"}'Account endpoints — GET /api/me, POST /api/keys, POST /api/checkout — take the same header. GET /api/offerings needs no key.
From CI, on every deploy
A Quick read of your staging site after each deploy costs $49 and takes well under an hour, so the people it simulates see a change before your customers do. Verify the staging hostname once (verification is per exact host, so use one stable staging name rather than a new preview URL per branch), keep your API key in a CI secret, and add a step like this. It orders the study and prints the link; the report arrives when it is done, and study_status says where it is.
- name: Quick read of staging
env:
PTX_KEY: ${{ secrets.PRODUCTTEX_KEY }}
STAGING: https://staging.example.com
run: |
design=$(curl -sf https://178-156-204-0.sslip.io/api/tools/design_study \
-H "Authorization: Bearer $PTX_KEY" -H "Content-Type: application/json" \
-d "{\"url\": \"$STAGING\", \"offering\": \"quick_read\",
\"customer_description\": \"owners of small design agencies in the US\"}")
cohorts=$(echo "$design" | jq -c .cohorts)
curl -sf https://178-156-204-0.sslip.io/api/tools/submit_study \
-H "Authorization: Bearer $PTX_KEY" -H "Content-Type: application/json" \
-d "{\"url\": \"$STAGING\", \"offering\": \"quick_read\", \"cohorts\": $cohorts,
\"label\": \"deploy $GITHUB_SHA\"}" | jq '{study_id, status, report_url}'From a coding agent with the MCP server connected, the same thing is one sentence: "order a Quick read of staging for owners of small design agencies, and tell me what the first three findings are when it is done." The agent should quote from quotable in get_report, not paraphrase the rates.
The order of calls
- register_domain — once per site. Proves you control it and records your authorisation.
- design_study — describe your customers in plain English; get back the groups we can actually draw and what we could not honour. Iterate freely; it does not run a study.
- submit_study — pick an offering, commit three guesses, name what "finished" looks like. Credits are debited here.
- study_status — poll. A study takes from about 40 minutes to a few hours.
- get_report — the findings, the sentences safe to repeat, and what the study cannot support.
list_offerings and account can be called at any point.
Tool reference
list_offerings
No arguments. Returns the offerings with their price in cents, what each includes, session count, duration and whether it is in beta; the credit packs; and the free-trial condition. Same content as GET /api/offerings and the pricing page.
account
No arguments. Returns your balance in cents, whether your free Quick read is still available, your domains with their verification state, and your studies.
register_domain
url- required. Any URL on the site.
acknowledgment- the authorisation sentence, echoed exactly (see below)
landing_pages- list of URLs people actually arrive on
audience- who the site is for, in a sentence
referrer_mix- where visitors come from; becomes each participant's reason for arriving
test_account- optional login for a test account, if you want the product behind sign-in studied
Call it first without acknowledgment. It refuses and returns acknowledgment_required: a sentence of the form
I control example.com and I authorize ProductTex to drive simulated visitor traffic against it. I understand simulated users may click, type, and submit forms, and that ProductTex is not liable for effects of that traffic on my site or its data.
Have the site's owner approve it, then call again with that exact text. A checkbox can be clicked by accident; a sentence returned word for word is a decision. The response is {domain, registered, verified}, plus verification with the three ways to prove control if it is not yet verified.
design_study
url- required. The page participants start on.
customer_description- required. Who your customers are, in plain English.
Returns suggested_cohorts — your audience resolved against the persona pool, with how many match and anything we could not honour — plus a control group (always included), a price estimate, advice, and next. A description the pool cannot match comes back as a refusal saying which part to loosen.
submit_study
url- required. Must be on a registered domain.
offeringquick_read,friction_study,device_split,accessibility_read,language_read,fix_checkorsecond_visitsealed_guesses- required. Exactly three: where you believe visitors struggle. Committed before any result exists; the report scores them.
milestone- text that appears when a visitor has finished, such as a phrase on the success page — or
no_milestonetrueto accept the funnel ending at "typed into the product"cohorts- optional. The cohorts from
design_study, if you edited them. arrival- optional. Why participants are here, e.g. "clicked a Google result for physician salary data"
max_spend_usd- optional. Refuses if the price exceeds it.
site_language- default
en. Only people who read it are drawn. label- optional, your own name for the study
Returns {study_id, …}. If the domain is verified the study is queued. If it is not, the study is held with status: "pending_verification" and the verification instructions; it starts as soon as the token is visible, and nothing else needs to be resubmitted.
study_status
study_id- required
Returns where the study is: pending_verification (re-checked on every call), queued, running with progress and instrument numbers, refused with the reason for each check, or done. It never returns findings mid-run: early rates from half a study mislead.
get_report
study_id- required
Returns report (the full report as JSON: rates with their floors and the spread between runs, themes with whether they held), quotable — sentences safe to repeat as written — and not_claimable — what this study cannot support. If you are an agent summarising a report for a person, quote from quotable and pass on not_claimable; do not restate a rate without its caveat.
Verifying your domain
We only run studies on sites whose owner has proved control. register_domain returns a token; show it in any one of three ways:
| Method | What to add |
|---|---|
| Well-known file | A plain text file at https://your-domain/.well-known/producttex.txt containing only the token |
| Meta tag | <meta name="producttex-verify" content="ptx-…"> in your homepage's <head>. Easiest on Webflow, WordPress or Framer. |
| DNS TXT record | your-domain. IN TXT "ptx-…". Propagation can take up to an hour. |
The response carries the exact content for all three, ready to paste. There is no separate confirm step: study_status and register_domain re-check on every call.
Refusals are data
When a study tool will not do what you asked, it answers HTTP 200 with refused: true and a reason written to be forwarded to a person or carried out by an agent. It is not an error and your code should not retry it as one.
{
"refused": true,
"reason": "exactly three sealed guesses are required — the site owner's suspected friction points, committed before any result exists. …"
}Some refusals carry what you need to proceed: acknowledgment_required (the sentence to echo), verification (the three proofs), estimate (when max_spend_usd is too low), or gates (which pre-flight check stopped a study and why). Show them verbatim. A study that refuses after you paid is refunded in full.
Report links
A finished study is readable at
https://178-156-204-0.sslip.io/r/<study_id>/<token>/report.html
https://178-156-204-0.sslip.io/r/<study_id>/<token>/report.pdf
https://178-156-204-0.sslip.io/r/<study_id>/<token>/report.jsonThe token in the link is the permission: anyone with the link can read the report, without an account. Share it with your team the way you would share a private document link. The dashboard and account list the links for each study.
Errors and limits
Errors that are not refusals are JSON {"error": code, "message": …} with a 4xx or 5xx status: 401 for a missing or revoked key, 429 when rate-limited, 503 for a feature that is not switched on (for example payments). If submissions refuse with a note that studies are paused, we have stopped the queue deliberately; nothing is charged.
Questions: the address on your invoice.