# OpenCode Usage Tracking With TelemHQ

Source HTML: https://telemhq.com/docs/opencode

OpenCode is an open-source local coding agent, not an API. Track it by adding an OpenCode plugin that sends TelemHQ metadata when sessions go idle or fail.

Use this guide when you want to understand local coding-agent activity across projects or teams: how often sessions run, which projects they touch, and where failures happen.

Create an ad hoc tracker with no schedule for normal OpenCode usage. Add a schedule only if the plugin runs from a predictable automation, such as a nightly OpenCode job.

## 1. Save Your Ping URL

Copy the ping URL from a TelemHQ tracker and make it available to the OpenCode process.

```bash
mkdir -p ~/.config/opencode/plugins
export TELEMHQ_OPENCODE_PING_URL="https://telemhq.com/ping/YOUR_TRACKING_TOKEN"
```

If OpenCode starts outside this shell, put the environment variable in the shell profile or process manager that launches OpenCode.

## 2. Add The Plugin

Save this as `~/.config/opencode/plugins/telemhq.js` to track every project, or as `.opencode/plugins/telemhq.js` inside one repo to track a single project. It listens for `session.idle` and `session.error` events and sends metadata only: tool name, status, event name, project, session id, and a hashed directory. It does not send prompts, commands, file contents, or model output.

```js
import { createHash } from "node:crypto";

export const TelemHQPlugin = async ({ directory }) => {
  const pingUrl = process.env.TELEMHQ_OPENCODE_PING_URL;
  const project = directory ? directory.split("/").pop() : undefined;

  const shortHash = (value) => {
    if (!value) return undefined;
    return createHash("sha256").update(value).digest("hex").slice(0, 12);
  };

  const sendPing = async (payload) => {
    if (!pingUrl) return;
    const body = {};
    for (const [key, value] of Object.entries(payload)) {
      if (value !== undefined) body[key] = value;
    }
    try {
      const res = await fetch(pingUrl, {
        method: "POST",
        headers: { "Content-Type": "application/json" },
        body: JSON.stringify(body),
      });
      if (!res.ok) throw new Error(`TelemHQ returned HTTP ${res.status}`);
    } catch (err) {
      if (process.env.TELEMHQ_HOOK_DEBUG === "1") console.error(`TelemHQ ping skipped: ${err}`);
    }
  };

  return {
    event: async ({ event }) => {
      if (event.type === "session.idle") {
        await sendPing({
          tool: "opencode",
          status: "success",
          event: "session.idle",
          project: project || "unknown",
          session_id: event.properties ? event.properties.sessionID : undefined,
          directory_hash: shortHash(directory),
          timestamp: new Date().toISOString(),
        });
      }
      if (event.type === "session.error") {
        await sendPing({
          tool: "opencode",
          status: "failed",
          event: "session.error",
          project: project || "unknown",
          session_id: event.properties ? event.properties.sessionID : undefined,
          directory_hash: shortHash(directory),
          timestamp: new Date().toISOString(),
        });
      }
    },
  };
};
```

OpenCode loads plugins automatically at startup, so no config change is needed. A missing ping URL silently skips sending, so the plugin never breaks your sessions.

## 3. Verify It Works

Restart OpenCode so it picks up the new plugin, complete one task, then open the tracker in TelemHQ and check Ping History. You should see a payload like this:

```json
{
  "tool": "opencode",
  "status": "success",
  "event": "session.idle",
  "project": "api",
  "session_id": "ses_abc123",
  "directory_hash": "d7a8fbb307d7",
  "timestamp": "2026-09-27T12:00:00.000Z"
}
```

OpenCode session events do not expose exact token and cost totals. For exact API usage, add token fields from your own wrapper when available.

## Privacy And Troubleshooting

The plugin sends usage metadata only. It does not send prompts, generated code, command arguments, file contents, model output, or raw local directory paths.

If no pings arrive, set `TELEMHQ_HOOK_DEBUG="1"` in the environment that launches OpenCode and watch its output for `TelemHQ ping skipped` messages.

## Related Guides

- Sending pings: https://telemhq.com/docs/integration
- Codex usage tracking: https://telemhq.com/docs/codex
- Claude Code usage tracking: https://telemhq.com/docs/claude-code
- OpenCode plugins reference: https://opencode.ai/docs/plugins
