Skip to content
Reflect API · v1

Put Reflect anywhere.

Everything the application shows you is available over HTTP as JSON, and trades can be pushed in as they close. No SDK, no webhook registration, no OAuth dance — one bearer token and ordinary REST.

Thirty seconds

Create a key in Settings, then:

curl
curl https://trader-reflect.com/api/v1/insights \
  -H "Authorization: Bearer rk_your_key_here"
javascript
const res = await fetch("https://trader-reflect.com/api/v1/insights", {
  headers: { Authorization: `Bearer ${process.env.REFLECT_KEY}` },
});
const { insights } = await res.json();

console.log(insights[0].headline);   // "You size up exactly when you should not."
console.log(insights[0].evidence);   // the numbers it rests on
console.log(insights[0].confidence); // 77
python
import os, requests

r = requests.get(
    "https://trader-reflect.com/api/v1/report",
    headers={"Authorization": f"Bearer {os.environ['REFLECT_KEY']}"},
)
report = r.json()
print(report["insights"][0]["headline"])

Authentication

Every request carries Authorization: Bearer rk_…. A key belongs to one account and can reach nothing else — there is no endpoint that takes a user id, so no key can be pointed at somebody else's data.

The key is shown once, at creation. Only a SHA-256 digest is stored, so a lost key cannot be recovered by you or by us — revoke it and make another. Keys come in two scopes: read reaches every GET endpoint, write also allows POST and DELETE. Use read wherever the caller only needs to display something.

Reading

Base URL https://trader-reflect.com/api/v1

GET
/me

The account behind the key, plus trade and import counts.

GET
/report

The full coaching report: findings, psychology scores, risk state, hourly and session breakdowns.

GET
/insights?category=risk

Findings only, ranked by cost.

GET
/performance

Equity curve, drawdown, expectancy, profit factor, streaks, per-instrument.

GET
/trades?limit=100&offset=0&from=2026-01-01&to=2026-12-31

Your trades, newest first.

GET
/days

Per-day totals: trades, net R, net P&L, wins.

GET
/journal?limit=100

What you wrote, newest first.

GET
/checkin?limit=60

Recorded mornings: sleep, and HRV where you have it.

GET
/reflection

The whole point: what you said set against what you did, which state preceded which execution, and what is still missing.

GET
/milestones

Behavioural milestones with what each one counted.

GET
/imports

Every batch of trades on the account, pushed or uploaded.

Writing

Needs a write-scoped key. Reflect never places orders — these endpoints only record trades that already happened.

POST
/journal

Write a day. Same date twice updates rather than duplicates — journalling can live in another product entirely.

POST
/checkin

Record a morning: sleepMinutes is required, hrv and restingHr optional. Where a wearable integration belongs.

POST
/trades

Push trades in. The natural integration point — call it as each trade closes.

POST
/imports

Upload a broker statement as CSV text and let Reflect parse it.

DELETE
/imports/:id

Remove a batch and every trade that came in with it.

DELETE
/journal/:date

Remove one day's entry.

POST /api/v1/trades
curl -X POST https://trader-reflect.com/api/v1/trades \
  -H "Authorization: Bearer rk_your_key_here" \
  -H "Content-Type: application/json" \
  -d '{
    "source": "my-bot",
    "trades": [
      {
        "instrument":  "XAUUSD",
        "direction":   "long",
        "openedAt":    "2026-08-04T09:15:00Z",
        "rMultiple":   1.8,
        "pnl":         180.00,
        "sizeR":       1.0,
        "followedPlan": true
      }
    ]
  }'
Required per trade
  • instrument — string
  • direction — "long" or "short"
  • openedAt — ISO 8601
  • rMultiple — number, negative is a loss
Optional
  • pnl — broker currency
  • sizeR — risk taken, defaults to 1
  • followedPlan — defaults to true
  • source — names the batch

Put a zone on openedAt. Without one it is read as UTC, and a timestamp meant as local time will land in a different trading session — which changes the analysis rather than failing loudly.

A batch is written whole or not at all. The response gives the importId, how many were accepted, and the index and reason for each one that was not — so a partial payload tells you exactly which trade to fix. Sending the same batch twice creates two batches: there is no deduplication yet.

Journalling somewhere else

Reflect reads two things: what your trades show, and what you said about them. The second does not have to be written here. Push the words in and read the reflection back — three calls, and the writing can live in whatever product your users already open every morning.

POST /api/v1/journal
curl -X POST https://trader-reflect.com/api/v1/journal   -H "Authorization: Bearer rk_your_key_here"   -H "Content-Type: application/json"   -d '{
    "date":    "2026-08-06",
    "entry":   "Stayed patient all morning and waited for the setup.",
    "emotion": "calm",
    "source":  "money-circle"
  }'

Word count and sentiment are derived from the text rather than accepted from you: two products computing them separately would drift. Whether the day made money comes from the trades, not the entry — somebody recording a good day that lost is exactly the gap worth surfacing, and letting the entry declare it would erase that. The same date twice updates rather than duplicates.

emotion takes one of ten words, returned as vocabulary by /reflection so you can offer the same set rather than inventing a parallel one. Naming a discrete state lowers its intensity; rating intensity on a scale does not, which is why there is no mood slider here. A word outside the set is dropped, not refused — the entry is still worth keeping.

GET /api/v1/reflection
{
  "selfReport": {
    "entries": 34, "unwritten": 6,
    "gaps": [{
      "date":    "2026-08-02",
      "claim":   "you described yourself as patient",
      "quote":   "Rough one but I stayed patient and never chased anything.",
      "reality": "3 of that day's entries came within five minutes of a loss",
      "kind":    "contradicted"
    }]
  },
  "emotions": [{ "emotion": "elated", "days": 7, "meanR": -0.77 }],
  "vocabulary": ["calm", "focused", "confident", "…"]
}

Embedding without code

If all you want is one figure inside Notion, a team dashboard or your own site, you do not need the API. Create an embed in Settings and paste the iframe.

iframe
<iframe src="https://trader-reflect.com/embed?t=re_your_token&theme=dark"
        width="360" height="220" frameborder="0"
        style="border:0;border-radius:12px"></iframe>

Four widgets: the current finding, your standing, your practice streak, and the equity curve. theme takes dark or light.

An embed token is not an API key and the difference matters. It travels in a URL that is visible in the hosting page's source, so it is read-only, bound to the single widget it was created for, revocable on its own, and rejected outright by every endpoint above. Anyone with the link sees that one figure — treat it as public.

Limits and errors

Rate limits, per key
  • 120 reads per minute
  • 30 writes per minute
  • 1000 trades per push
  • 2 MB per request body

Every response carries X-RateLimit-Remaining and X-RateLimit-Reset. A 429 also carries Retry-After.

Status codes
  • 400 — malformed body
  • 401 — missing or revoked key
  • 403 — read key used for a write
  • 404 — no such endpoint or record
  • 422 — well-formed but unusable
  • 429 — rate limited
Error shape
{
  "error": {
    "code": "read_only_key",
    "message": "This key is read-only. Create a write key in Settings."
  }
}

Branch on code, show message to a person. CORS is open, so a browser can call this directly — but a key in front-end JavaScript is a key you have published. Use a read key, or keep it on your server.

Reflect reads. It never places an order.Create a key →