ProductTex

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

  1. register_domain — once per site. Proves you control it and records your authorisation.
  2. 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.
  3. submit_study — pick an offering, commit three guesses, name what "finished" looks like. Credits are debited here.
  4. study_status — poll. A study takes from about 40 minutes to a few hours.
  5. 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.
offering
quick_read, friction_study, device_split, accessibility_read, language_read, fix_check or second_visit
sealed_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_milestone
true to 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:

MethodWhat to add
Well-known fileA 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 recordyour-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.json

The 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.