Documentation

Integration

Product analytics for AI agents. Two ways in: a single REST call, or a zero-code base-URL swap. Both are idempotent and off your critical path.

Base URL https://app.dimies.com AI agents spec /llms.txt

Authentication

Create API keys in Settings. Every request needs one:

  • Ingest API: Authorization: Bearer dm_live_YOUR_KEY
  • LLM proxy: the x-dimies-key: dm_live_YOUR_KEY header

Read the key from an environment variable (DIMIES_API_KEY) — never hardcode it.

Integrate with an AI coding agent

Dimies serves a machine-readable integration spec at https://app.dimies.com/llms.txt. Paste this prompt into your coding agent (Claude Code, Cursor, Copilot) inside the repo you want instrumented:

promptIntegrate Dimies conversation analytics into this codebase.
Fetch and follow the spec at https://app.dimies.com/llms.txt — send each completed
conversation (full message list, fire-and-forget) to the ingest API, wrap
LLM/tool calls as trace spans where obvious, read DIMIES_API_KEY and
DIMIES_URL from env, and add both to .env.example. My key is in the
DIMIES_API_KEY environment variable. Verify with:
npx dimies test --key $DIMIES_API_KEY --url https://app.dimies.com

Prefer a CLI? npx dimies init scaffolds the tracking helper (Node or Python) and npx dimies test verifies your key end-to-end.

Ingest API

POST /api/v1/conversations

Send the full conversation so far each time — repeated calls with the same conversation_id replace previous state, so you never track diffs. Call it after each exchange, on a timer, or once when the conversation ends. Analysis runs automatically once a conversation is closed or has been quiet for ~10 minutes.

Request body

json{
  "conversation_id": "string   — your ID; upsert key (optional; derived if omitted)",
  "user_id":         "string   — your end-user ID (optional)",
  "channel":         "string   — e.g. web, whatsapp, voice (optional)",
  "closed":          "boolean  — true = analyze immediately (optional)",
  "metadata":        "object   — anything you want to keep (optional)",
  "messages": [
    { "role": "user | assistant | system | tool", "content": "string", "timestamp": "ISO-8601 or epoch (optional)" }
  ]
}

Example

bashcurl -X POST https://app.dimies.com/api/v1/conversations \
  -H "Authorization: Bearer dm_live_YOUR_KEY" \
  -H "Content-Type: application/json" \
  -d '{
    "conversation_id": "chat_8271",
    "user_id": "user_42",
    "closed": true,
    "messages": [
      {"role": "user", "content": "How do I reset my password?"},
      {"role": "assistant", "content": "Go to Settings → Security → Reset password."}
    ]
  }'

Response

json{ "ok": true, "conversation_id": "chat_8271", "status": "pending" }

LLM proxy (zero-code capture)

Swap your LLM client's base URL and every conversation is captured automatically — no other code. The proxy forwards every request to the provider unchanged (streaming included) and captures the conversation on the way through. Your provider API key is passed through and never stored.

OpenAI

javascriptconst openai = new OpenAI({
  apiKey: process.env.OPENAI_API_KEY,
  baseURL: "https://app.dimies.com/api/v1/proxy/openai/v1",
  defaultHeaders: {
    "x-dimies-key": "dm_live_YOUR_KEY",
    "x-dimies-conversation-id": chat.id,   // optional but recommended
    "x-dimies-user-id": user.id,           // optional
  },
});

Anthropic

javascriptconst anthropic = new Anthropic({
  apiKey: process.env.ANTHROPIC_API_KEY,
  baseURL: "https://app.dimies.com/api/v1/proxy/anthropic",
  defaultHeaders: { "x-dimies-key": "dm_live_YOUR_KEY" },
});

Supported upstreams today: OpenAI and Anthropic only. Other OpenAI-compatible providers (Groq, Together, OpenRouter, LiteLLM, Ollama, Moonshot/Kimi) are coming soon — for those, use the ingest API + trace instead.

Trade-off: the proxy sits on your request path. If you prefer zero added risk, use the ingest API — it's one fire-and-forget call and can never affect your users.

Tracing (optional)

Add a trace array to the same ingest call. Spans nest via parent_id and appear as an execution tree on the conversation page, with latency, tokens, cost and errors. Traces merge: spans upsert by id, so you can send each turn's spans incrementally — earlier spans are never lost, and resending is idempotent. Conversations captured through the LLM proxy get an llm span automatically.

Span fields

json{
  "trace": [
    { "id": "root", "name": "handle-message", "type": "other",
      "started_at": 1720000000000, "ended_at": 1720000004100 },
    { "id": "s1", "parent_id": "root", "name": "search-kb", "type": "retrieval",
      "started_at": 1720000000100, "ended_at": 1720000000900,
      "input": "refund policy", "output": "3 documents" },
    { "id": "s2", "parent_id": "root", "name": "claude.messages", "type": "llm",
      "started_at": 1720000001000, "ended_at": 1720000003900,
      "model": "claude-haiku-4-5", "tokens_in": 1240, "tokens_out": 85, "cost": 0.0017,
      "input": "…prompt…", "output": "…completion…" },
    { "id": "s3", "parent_id": "root", "name": "issue-refund", "type": "tool",
      "status": "error", "error": "Stripe API timeout after 10s" }
  ]
}

All fields optional except name. Types: llm, tool, retrieval, function, other. Timestamps accept epoch ms or ISO-8601. Max 200 spans per conversation.

Attach the payload. Clicking a span shows its input, output, error and metadata; if all four are empty it reads "No payload recorded for this span." On an llm span, set input to the prompt/messages and output to the completion; on a tool span, the arguments and result; on a retrieval span, the query and the documents. Field names are exact — sending prompt/completion/response/messages instead of input/output gets silently dropped, and the panel shows no payload.

Give every span a stable id (e.g. tool-refund-1). It's the merge key: resend the same id to update a span — for example, create it when a tool starts, then resend it with status: "error" if it fails — and it guarantees no duplicates when you resend. Omit id and spans are de-duped by name + timestamps instead.

Merge is field-level. A resend updates only the fields you include; omitting a field keeps its stored value, so resending { id, status: "error" } never wipes the input/output/model you sent earlier. To clear a field, send it explicitly as null.

What happens after ingestion

  1. Conversations wait until they're closed or idle (~10 min), so Dimies analyzes whole conversations, not fragments.
  2. Each is classified once: topic, intent, in/out of scope (based on your agent description in Settings), resolution, sentiment, frustration, language, and safety flags with verbatim evidence.
  3. Topics aggregate automatically — no taxonomy to maintain. Everything lands in the dashboard within minutes.

CLI

shellnpx dimies init     # scaffold a tracking helper (Node or Python) + .env.example
npx dimies test     # send a test conversation, verify your key end-to-end
npx dimies prompt   # print an integration prompt for AI coding agents
npx dimies spec     # print the machine-readable spec (llms.txt)

Package: dimies on npm.