Start Here

Add Logga With Your AI Agent

Copy one prompt into Claude Code, Cursor, or any coding agent and have Logga wired into your project in a few minutes.

Logga has no SDK to install. Sending an event is one HTTPS request, which makes it a good fit for the coding agent you already have open: it can read your codebase, work out what is worth tracking, and write the calls itself.

Copy the prompt below, paste it into your agent inside your project, and answer its questions. It is written to produce the integration a careful engineer would write, not the one an agent produces when it is only told "add analytics".

Before you start

You need an API key. Create one in the iOS app or the dashboard: Settings → API Keys → Create key. Copy it when it appears, because Logga stores only a hash: so that response is the only place the full key exists.

If the key will live anywhere a user can reach it (a mobile app, a desktop app, a browser bundle), bind it to a single project when you create it and leave the scopes at their default. It can then write its own project's events and read nothing at all. See Keys for a distributed client.

Put the key in your environment before you run the prompt:

bash
export LOGGA_API_KEY="lg_sk_your_api_key"

The prompt

Integration prompt
Add Logga event tracking to this project.

Logga is a hosted event-tracking service: one HTTPS endpoint, no SDK. The full
reference is at https://logga.sh/docs. Read
https://logga.sh/docs/getting-started/quickstart and
https://logga.sh/docs/backend/api-reference if anything below is unclear.

## The API

POST https://api.logga.sh/v1/events
  Authorization: Bearer $LOGGA_API_KEY
  Content-Type: application/json

  {
    "channel": "billing",              // required, ≤64 chars, created on first use
    "event": "subscription_renewed",   // required, ≤128 chars
    "level": "success",                // debug | info | success | warning | error (default info)
    "metadata": { "amount": 22.75 },   // optional JSON object, ≤10KB
    "actor": {                         // optional, identifies the end user
      "id": "user_123",                // your stable internal id
      "name": "Ada Lovelace",
      "email": "[email protected]",
      "properties": { "plan": "pro" }  // strings/numbers/booleans, ≤50 keys per user; merged into the
                                       // user's existing properties, send a key as null to remove it
    },
    "tags": ["stripe"],                // optional, ≤10 strings
    "idempotencyKey": "invoice_abc123" // optional, globally unique, makes retries safe
  }

Returns 201 with the created event; if actor.properties broke a limit, the event is
still stored and the body carries a "warnings" array. 422 means the body failed
validation and the response names the field. 429 means the key passed 1000 events per 15 minutes.

## What to build

1. Read the key from the LOGGA_API_KEY environment variable. Never hardcode it,
   never commit it, never ship it to a browser bundle. If this project is
   frontend-only, send events from its backend or from a route handler, not from
   the client.

2. Write ONE small module in the language and style of this codebase, exposing a
   single function (logEvent / log_event / LogEvent as fits). It must:
   - never throw and never propagate an error to the caller;
   - not block the path it is called from: fire and forget, with a timeout of a
     couple of seconds;
   - accept an option to await the send instead, and use it for events that
     report a failure the process may not survive. A fire-and-forget error
     event is lost exactly when it mattered: the process exits before the
     request leaves;
   - be a no-op when LOGGA_API_KEY is unset, so local runs and tests stay silent
     and nobody needs a key to work on the project;
   - retry at most once, and only on a network error or a 5xx.
   Tracking must never be able to break the thing it is tracking.

3. Read the codebase and propose what to instrument. Show me a short table of
   channels and events with the metadata each one carries, then implement it.

## Conventions to follow

- A channel is an area of the product, lowercase, stable, and there should be
  few of them: auth, billing, jobs, errors. Not one per event type.
- An event name is snake_case and past tense: user_signed_up, payment_failed,
  export_finished.
- level is severity, not topic. An error in billing is level "error" in channel
  "billing". Do not create an "errors" channel for it.
- metadata carries the few fields someone would want at 3am while looking at
  this event: ids to search by, amounts, the reason a thing failed. No secrets,
  no tokens, no full request bodies, no personal data beyond what actor already
  identifies.
- actor.id is my stable internal user id, the same one across every event.
- Use idempotencyKey wherever the same event can legitimately be sent twice:
  webhook handlers, retried jobs, at-least-once queues. Namespace it with the
  event name, because it is unique across the whole workspace and a bare record
  id like "ord_1" will eventually collide with something else's.

## What NOT to do

- Do not instrument everything. Pick the events that would change what I do:
  money, signups, failures that need a human, long jobs finishing.
- Do not mirror the logger. Logga is not stdout: no per-request logging, no
  debug traces, no loops that emit one event per item.
- Do not add a dependency for this. Use whatever HTTP client the project already
  has, or the standard library.

## When you are done

- Add one runnable check: a script or test that posts an event named
  integration_check to the channel "system" and asserts a 201.
- Tell me the channel names you created. Notifications are off by default for
  every new channel, so I need the list to switch on the ones that matter.

What the prompt is doing

Each rule in it is there because the obvious version of this integration goes wrong in a specific way.

Fire and forget, never throw. The first thing an agent writes is await log(...) in the middle of a checkout. Then Logga has a slow minute and your checkout has a slow minute. Tracking is allowed to lose an event; it is not allowed to lose a sale.

No-op without a key. Otherwise every contributor needs a key to run the test suite, and someone eventually commits one to make the red go away.

Few channels, past-tense events. Channels are what you subscribe to for notifications and what the app's sidebar lists. Thirty channels means the list is useless and every one of them is muted a week later.

Severity is not topic. level decides what wakes you up. If failures live in an errors channel instead of carrying level: "error", you cannot say "notify me on errors in billing" without saying it twice.

Idempotency keys on webhooks. Stripe will send the same event twice, and your revenue chart will show it twice.

Awaiting the error event. This one came out of testing the prompt rather than out of an opinion. An agent following the fire-and-forget rule instrumented a webhook that then threw, the process died before the HTTP request left, and the payment_failed event never arrived. Everything that reports a failure the process may not survive has to be awaited, with a short timeout: the crash is the one moment you cannot afford to lose the event.

After the agent is done

  1. Open Activity in the iOS app or Events in the dashboard. Your first events should be there within a second or two.
  2. Turn on notifications for the channels worth interrupting you: they are off by default, which is why a fresh project is quiet. See Rules, Reports, and Notifications to narrow which events in a channel push.
  3. If you want the agent to be able to read the data back, not just write it, give it an MCP connector: AI Connectors (MCP).

Next steps