> ## Documentation Index
> Fetch the complete documentation index at: https://docs.trycurious.ai/llms.txt
> Use this file to discover all available pages before exploring further.

# Quickstart

> Make your first decision and attribute your first reward in a few minutes with a test key, using the server SDK or plain HTTP.

By the end of this page you will have declared an element with a control and a reward, served a decision to a user, reported the outcome Curious learns from, and seen it in the dashboard. Everything runs under a test key, so nothing here touches live data.

### 1. Get a test key

In the [dashboard](https://app.trycurious.ai), open **Settings** and create a **secret test key**. It starts with `curious_sk_test_`. Put it in your environment as `CURIOUS_SECRET_KEY`; it is shown once.

A test key runs the whole product against shadow users: real declarations, real variants, real decisions, excluded from live learning and live metrics. See [Keys and environments](https://docs.trycurious.ai/guides/keys.md).

### 2. Decide what to show

The first call for an element declares it: the **control** is what you show today, the **rewards** are what success means, and `element_type` tells Curious what kind of content it is writing. The response is the content to render.

> [tab] HTTP
>
> ```bash theme={null}
> curl https://api.trycurious.ai/v1/decide \
>   -H "Authorization: Bearer $CURIOUS_SECRET_KEY" \
>   -H "Content-Type: application/json" \
>   -d '{
>     "element": "welcome_email_subject",
>     "element_type": "email_subject",
>     "user_id": "user_123",
>     "control": "Welcome to Acme",
>     "rewards": [
>       { "name": "email_clicked", "window_seconds": 86400 }
>     ]
>   }'
> ```
>
> ```json theme={null}
> {
>   "content": "Welcome to Acme",
>   "arm_id": "3f1c2b6e-0000-4000-8000-00000000a000",
>   "arm_type": "control",
>   "decision_id": "8a7b6c5d-0000-4000-8000-00000000d001",
>   "experiment_id": "1e2d3c4b-0000-4000-8000-00000000e001",
>   "experiment_status": "initializing"
> }
> ```

> [tab] Node SDK
>
> > [!NOTE]
> >
> > `@trycurious/node` is built and tested but not yet published to npm, so this tab does not run yet. Use the HTTP tab until it is; everything the SDK does is documented on [Raw HTTP](https://docs.trycurious.ai/sdks/http.md).
>
> ```bash theme={null}
> npm install @trycurious/node
> ```
>
> In a Supabase Edge Function or Deno: `import { Curious } from "npm:@trycurious/node@0"`. Node 22 or newer, Bun and Cloudflare Workers are supported.
>
> ```ts theme={null}
> import { Curious } from "@trycurious/node";
>
> const curious = new Curious({ apiKey: process.env.CURIOUS_SECRET_KEY! });
>
> const result = await curious.decide({
>   element: "welcome_email_subject",
>   userId: "user_123",
>   fallback: "Welcome to Acme",
>   declaration: {
>     elementType: "email_subject",
>     control: "Welcome to Acme",
>     rewards: [{ name: "email_clicked", windowSeconds: 86_400 }],
>   },
> });
>
> sendEmail({ subject: result.content });
> // result.served is "curious" or "fallback"; result.armId says which arm.
> ```

The first decisions serve the control while Curious generates its first variants; `experiment_status` moves from `initializing` to `exploring` when they are live. Send the same declaration on every call: an identical declaration is a no-op.

### 3. Report the outcome

When the user does the thing, send the event. Curious attributes it to the decision that preceded it, within the reward's window.

> [tab] HTTP
>
> ```bash theme={null}
> curl https://api.trycurious.ai/v1/events \
>   -H "Authorization: Bearer $CURIOUS_SECRET_KEY" \
>   -H "Content-Type: application/json" \
>   -d '{
>     "user_id": "user_123",
>     "name": "email_clicked",
>     "id": "evt_8f3a"
>   }'
> ```
>
> ```json theme={null}
> { "status": "success" }
> ```
>
> `id` is optional and makes a retry safe: send the same request again and the response is `{ "status": "success", "duplicate": true }`.

> [tab] Node SDK
>
> ```ts theme={null}
> curious.track({ userId: "user_123", name: "email_clicked" });
>
> // Hand delivery to your runtime before it exits or freezes:
> await curious.flush(); // jobs, scripts, graceful shutdown
> // EdgeRuntime.waitUntil(curious.flush()); // Supabase Edge Functions
> // ctx.waitUntil(curious.flush());         // Cloudflare Workers
> ```

### 4. Confirm it worked

Open the [dashboard](https://app.trycurious.ai). The element is there with its experiment, the control, and the decision and event you just sent.

When the first variants appear, each with the hypothesis behind it, they wait for your approval before serving: `options.approval` defaults to `manual`. Approve them in the dashboard, or declare `"options": { "approval": "auto" }` to let generated variants serve as soon as they exist. See [Approval](https://docs.trycurious.ai/concepts/approval.md).

## Where next

> [card] [Serve a decision](https://docs.trycurious.ai/guides/decide.md)
>
> The user buckets, the declaration fields, the response, and what publishable keys may send.

> [card] [Track outcomes](https://docs.trycurious.ai/guides/events.md)
>
> Event names, values, timestamps, idempotent ids and the batch form.

> [card] [Fallbacks and timeouts](https://docs.trycurious.ai/guides/fallbacks.md)
>
> What a correct delivery path does when Curious is slow or down.

> [card] [Go live](https://docs.trycurious.ai/guides/go-live.md)
>
> The checklist for switching from a test key to a live one.

The nouns behind these calls (elements, declarations, arms, rewards) are on [The model](https://docs.trycurious.ai/concepts/model.md).


This documentation is built and hosted on [Mintlify](https://mintlify.com), a developer documentation platform.