← Files KnockARCHIVED FILE

skills/knock-setup/rules/import-users.md

5.41 KB · Oct 2, 2026 · 00:17 UTC

↓ Download file

---
title: Import users into Knock
description: Research the codebase, choose a stable user id, and prepare a bulk-identify script — do not run in production unless asked
tags:
  - setup
  - users
  - identify
  - production
category: knock-setup
last_updated: 2026-07-09
---

# Import users into Knock

Extension rule — **not required on the first setup pass**. Use when preparing for production, or when the user asks to import / sync users into Knock. Can sit on a later production checklist.

## Why this matters

Notification recipients must exist in Knock (or be identified inline at trigger time). For a real product, users need to live in Knock so workflows can address them by a stable id. See [Users in Knock](https://docs.knock.app/concepts/users) and [identifying recipients](https://docs.knock.app/managing-recipients/identifying-recipients).

Knock’s bulk path for an initial import is [bulk identify users](https://docs.knock.app/api-reference/users/bulk/identify) (`POST /v1/users/bulk/identify`, up to 1,000 users per request). Prefer your internal primary key as `id` — it cannot contain `/` or `#`, max 256 chars, and **cannot be changed later**.

## 1. Confirm the primary id (ask first)

Before researching or writing code, ask which field should be Knock’s primary user `id` for later identification. Put the question **last**, on its own line, and bolded.

Recommend the product’s durable internal user id (database primary key / UUID). Email is usually a bad primary id (it can change). Only use email or another field if the user explicitly chooses it.

**What should be the primary Knock user id — your internal user id, email, or something else?**

Do not continue until they answer.

## 2. Research the codebase

Map how users exist in this app:

- Where user records live (DB table/model, auth provider, CRM)
- How to list or page through users safely (ORM, API, export)
- Fields available for Knock: at least `id`; typically `email`, `name`; optionally `phone_number`, `avatar`, `timezone`, custom traits
- Existing env / secrets patterns (`.env`, `.env.local`, secret managers)
- Preferred language/runtime for one-off scripts (Node, Python, etc.)

Keep findings short. Prefer reusing existing data-access helpers over inventing a parallel query path.

## 3. Secret API key in an env var

Bulk identify needs a **secret** API key (`sk_*`) for the target Knock environment — not a public `pk_*` key. Create/copy it from the Knock dashboard (Developer → API keys for that environment).

Use env var name: `KNOCK_API_KEY`.

Offer a command that opens the dashboard and waits for the user to paste the key into `.env.local` (create the file if needed). Prefer running this automatically when the shell allows interactive input:

```bash
open "https://dashboard.knock.app/" && \
  printf "Paste your Knock secret API key (sk_…) for the target environment, then press Enter:\n" && \
  read -r KNOCK_API_KEY && \
  touch .env.local && \
  grep -q '^KNOCK_API_KEY=' .env.local 2>/dev/null \
    && sed -i.bak "s|^KNOCK_API_KEY=.*|KNOCK_API_KEY=${KNOCK_API_KEY}|" .env.local \
    || echo "KNOCK_API_KEY=${KNOCK_API_KEY}" >> .env.local && \
  echo "Saved KNOCK_API_KEY to .env.local"
```

If `open` is unavailable, print the dashboard URL and still run the `read` + write steps. Never commit `.env.local` or echo the key back in chat.

## 4. Write a prepare / bulk-identify script

Add a script that matches the repo’s conventions (language, package manager, lint style). It should:

1. Load `KNOCK_API_KEY` from the environment (fail clearly if missing).
2. Load users from the app’s real data source (reuse existing patterns).
3. Map each row to Knock’s identify shape: required `id` (the field they chose), plus `email` / `name` / other useful fields when present.
4. Call bulk identify in batches of **at most 1,000** users ([API reference](https://docs.knock.app/api-reference/users/bulk/identify)).
5. Default to a **dry-run** that prints batch counts and a sample payload **without** calling the API.
6. Only perform live API calls when an explicit flag is passed (e.g. `--execute`) **and** the user has asked to run it.
7. Target development first unless the user explicitly names production.

Example Node shape (adapt to the stack):

```js
// dry-run by default; pass --execute only when the user asks
import Knock from "@knocklabs/node";

const knock = new Knock({ apiKey: process.env.KNOCK_API_KEY });
const BATCH = 1000;

async function identifyBatch(users) {
  await knock.users.bulk.identify(users);
}
```

Also document the ongoing production pattern: identify on signup / profile update (API or inline identify on workflow trigger) so Knock stays in sync after the initial import — see [identifying recipients](https://docs.knock.app/managing-recipients/identifying-recipients).

## 5. Production safety

- **Do not run the import against production** unless the user explicitly asks to.
- Prefer dry-run → development execute → production execute (only on explicit request).
- Remind them users are **per environment**; a development import does not populate production ([Users FAQ](https://docs.knock.app/concepts/users)).
- After a successful import, suggest verifying a few users in the Knock dashboard under Users.

## Reference docs

- [Users concept](https://docs.knock.app/concepts/users)
- [Identifying recipients](https://docs.knock.app/managing-recipients/identifying-recipients)
- [Bulk identify users](https://docs.knock.app/api-reference/users/bulk/identify)

SHA-256: 3d1c21d8e5b4bd94d2837fd1902318b94870fadb788cab0db2915519433b3fc6