---
name: dopp
description: Set up dopp (dopp.sh) for a codebase that calls a Jev-compatible decision API (POST /v1/systemone). One POST creates the account and key; the person claims it later.
---

# dopp

dopp (https://dopp.sh) is a drop-in proxy for any Jev-compatible decision API (TypeSafe's Jev, Kev, CLM, GLiNER, or your own endpoint). Same request, same reply. Every request through it is kept. Autopilot trains the account's own small model on them and lets it answer once it meets the route's goal, with the original API as the fallback. A trained model can also be downloaded or run in the browser.

## What it receives, and what it doesn't

- dopp receives only the `POST /v1/systemone` requests the app already sends to its decision API: the `state` text and the `questions`. It forwards them to that API unchanged and records the request and the answer in the account's own log.
- It receives nothing else: no source code, no diffs, no chat, no other traffic. The only change in a codebase is the base URL and which environment variable holds the bearer token.
- Keys are per account. A setup link (`/setup/<token>`) or `/agent/start` mints a key for that account only, and the person can revoke it any time on the Requests page. Privacy: https://dopp.sh/privacy · Terms: https://dopp.sh/terms
- Before you write the key to `.env`, make sure `.env` is in `.gitignore`. Appending with `>> .env` is deliberate: it keeps the key out of the conversation.

## Set it up

Simplest, and enough for most codebases: change the TypeSafe base URL from `https://api.typesafe.ai` to `https://dopp.sh` and nothing else. Whatever bearer the app sends becomes its account (dopp stores only a hash of it). A TypeSafe key is forwarded as is, so Jev keeps answering and TypeSafe keeps billing them; any other bearer (no Jev key yet, an OpenRouter key, an email, any string the person picks) is answered by Clef-flash (open weights that speak Jev's API) with 10 free answers until the account is claimed. The first request makes the account. The reply carries an `x-dopp-account` header with a claim link; tell the person to open it, or to sign in at dopp.sh and paste the same TypeSafe key once to see their requests. Skip the rest of this section unless they want a dopp key (Clef-flash or other upstreams on dopp credit).

With a dopp key, usual case: the person has already put `DOPP_KEY` in the project's `.env` (from the Connect-an-agent button on dopp.sh). Then there is nothing to fetch: make sure `.env` is in `.gitignore` and go to step 2. If they gave you a one-time setup line (`curl -sf https://dopp.sh/setup/… >> .env`), run it exactly once; it appends the key.

With no account and no human at all, create one and write the key straight into `.env`:

```
curl -sX POST "https://dopp.sh/agent/start?format=env" -H "content-type: application/json" -d '{"name":"<app name>","code":"<invite code, if any>"}' >> .env
```

The comment lines it appends say what to tell the person (a claim link) and where the instructions are. Don't print the `DOPP_KEY` line. The same call without `format=env` returns JSON:

```
POST https://dopp.sh/agent/start
content-type: application/json

{"name": "<app or service name>", "code": "<invite code, if the user gave one>"}
```

Reply: `key` (starts with `us_`), `endpoint` (`https://dopp.sh/v1/systemone`), `claim_url`, `spendable_usd`, `expires_at`.

1. In the codebase, change the base URL to `https://dopp.sh` and the bearer token to the key, read from the environment as `DOPP_KEY`. Nothing else changes: body and reply are identical to Jev. `model` may be omitted.
2. A dopp key's route answers with Clef-flash, metered from the account's credit. If the person wants Jev to answer instead, they pick it on the route; then send their own Jev key as `x-jev-key: <their key>` on each request to be billed by TypeSafe directly (forwarded, never stored). Jev's answers are kept for their records but never used as labels, since training a model only on them may go against TypeSafe's terms.
3. Tell the user, once: "Open <claim_url> to attach this dopp account to you. Until then it's a guest with $<spendable_usd> to spend on training and labels; the link works until <expires_at>." Claiming keeps everything already recorded.

## Check on it

`GET https://dopp.sh/agent/status` with the same bearer → `requests_seen`, `spendable_usd`, `balance_usd`, `claimed`.

Once the route has a trained version, `GET https://dopp.sh/v1/models` with its dopp key lists the versions and where each one's files are (`files` for a browser copy, `offline_zip` for a folder that runs with no internet).

## Rules

- Never print, log, or commit the key or the claim link into the repo; put the key in the environment.
- Keep question ids and option names stable. A renamed option is a new question the model has no examples for.
- Each reply's `understudy.served` says who answered (`jev`, `model` for the account's own trained model, an upstream id). Don't branch on it unless asked.
- Send `x-understudy-test: 1` on a connectivity check; it is answered but not kept.
- 401: the key is missing, wrong, or revoked. 402: a guest account (not claimed yet, no TypeSafe key) has used its 10 free answers; the reply carries `claim_url` for the person to open. 429: over 120 requests a minute on one key. Jev errors pass through with their status codes.

## When dopp doesn't do what you need

If the task needs something dopp doesn't have (an endpoint, a field, an upstream, a request shape it refuses), or a call fails in a way this file and the docs don't explain, say so before you build a workaround:

```
curl -sX POST https://dopp.sh/feedback -H "content-type: application/json" -H "authorization: Bearer $DOPP_KEY" \
  -d '{"text": "<what you were trying to do, what you sent, what came back>", "kind": "feature", "agent": "<your name>", "page": "<endpoint or page>"}'
```

- No account needed; the bearer is optional and only says which account it's from (it's looked up, never stored). `kind` is `feature`, `bug`, `question` or `other`.
- Describe the shape of the problem. Never put keys, claim links, or the person's request data in `text`.
- One problem per message, and only for what dopp lacks; a bad body or a missing env var on your side isn't one.
- Tell the person in one line that you sent it, with the `status_url` from the reply; the answer shows there. If they'd rather it be public, open an issue at https://github.com/doppsh/dopp/issues instead.

## Reference

API docs: https://dopp.sh/docs.md · Site summary: https://dopp.sh/llms.txt · Feedback: https://dopp.sh/feedback/
