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

# Quickstart

> From a new account to your first identified contact, in one sitting.

This walks the whole path once: create the account, grab a key, identify a
contact, and read it back. Everything below runs against
`https://api.userkit.dev`.

```bash theme={null}
export API="https://api.userkit.dev"
```

## 1. Create your account

Sign up at [app.userkit.dev](https://app.userkit.dev). Sign-up creates the user,
the organization, your owner membership, both environments and their publishable
keys in a single transaction, then drops you into the panel.

<Note>
  Every organization is born with a **live** and a **test** environment. What
  separates them is the key you present, never a parameter — start in test and
  nothing you do here shows up in production's numbers.
</Note>

## 2. Mint an API key

In the panel, **Settings → API keys**, create a key in the **test** environment.
A key is born inside one environment and never leaves it; the environment is
recorded in the prefix itself (`uk_sk_test_…`).

<Warning>
  The key's value is shown exactly once. Only its SHA-256 hash is stored, so a lost
  key is replaced, never recovered.
</Warning>

```bash theme={null}
export UK_KEY="uk_sk_test_a91c…"
```

## 3. Check the key

The first call every integrator makes. What comes back is the environment the
**key** belongs to — nothing you send can name a different one.

```bash theme={null}
curl -s $API/v1/me -H "Authorization: Bearer $UK_KEY"
```

```json Response theme={null}
{
  "organization": { "id": "…", "name": "Analytical Engine" },
  "environment": { "id": "…", "kind": "test" },
  "api_key": { "id": "…", "prefix": "uk_sk_test_a91c" }
}
```

## 4. Identify a contact

Now the customer plane. Send whatever identities your backend knows; at least one
of `external_id`, `email` or `anonymous_id` is required.

```bash theme={null}
curl -s $API/v1/contacts \
  -H "Authorization: Bearer $UK_KEY" \
  -H 'Content-Type: application/json' \
  -d '{
    "external_id": "user_8421",
    "email": "grace@example.com",
    "name": "Grace Hopper"
  }'
```

```json Response — 201 theme={null}
{
  "contact": {
    "id": "3f9a…",
    "name": "Grace Hopper",
    "email": "grace@example.com",
    "identified": true,
    "email_verified": false,
    "attribution": { "utm_source": null, "...": null },
    "first_seen_at": "2026-07-29T12:00:00Z"
  },
  "created": true
}
```

Call it again with the same `external_id` and you get `200` with
`"created": false` — identify is create-or-update, not create.

<Note>
  `email_verified` stays `false` until an actual link is clicked. Passing an email
  to the API records an attribute; it does not prove one.
</Note>

## 5. Read it back

```bash theme={null}
curl -s $API/v1/contacts/3f9a… -H "Authorization: Bearer $UK_KEY"
```

The detail read adds the contact's identity edges — the values that resolve to
it:

```json theme={null}
{
  "contact": {
    "id": "3f9a…",
    "identities": [
      { "kind": "external_id", "value": "user_8421", "created_at": "…" },
      { "kind": "email", "value": "grace@example.com", "created_at": "…" }
    ],
    "...": "..."
  }
}
```

Try the same id with a **live** key and you get `404`. That is environment
isolation working: the key's environment scopes the read, and the 404 does not
reveal whether the row exists somewhere else.

## Where to go next

<CardGroup cols={2}>
  <Card title="Contacts" icon="users" href="/en/concepts/contacts">
    Identity edges, attribution and how merges work.
  </Card>

  <Card title="Choosing an auth mode" icon="route" href="/en/customer-auth/modes">
    Hosted or federated — decide before you wire the front end.
  </Card>

  <Card title="Environments" icon="layer-group" href="/en/concepts/environments">
    What live and test actually separate.
  </Card>

  <Card title="Roles and permissions" icon="shield-halved" href="/en/concepts/roles-and-permissions">
    Give your teammates less than everything.
  </Card>
</CardGroup>
