Users
Read Users
Query and retrieve user data in HelloJohn — individual users, lists, and search.
Read Users
Retrieve user data using the HelloJohn API or SDK. All read operations require a secret key on the backend.
Get a Single User
By ID
curl "https://api.hellojohn.dev/v1/users/usr_01HABCDEF123456" \
-H "Authorization: Bearer sk_live_abc123" \
-H "X-Tenant-ID: tnt_01HABCDEF654321"By Email
curl "https://api.hellojohn.dev/v1/users?email=alice@example.com" \
-H "Authorization: Bearer sk_live_abc123" \
-H "X-Tenant-ID: tnt_01HABCDEF654321"By External ID
curl "https://api.hellojohn.dev/v1/users?external_id=customer_42" \
-H "Authorization: Bearer sk_live_abc123" \
-H "X-Tenant-ID: tnt_01HABCDEF654321"Response:
{
"id": "usr_01HABCDEF123456",
"email": "alice@example.com",
"email_verified": true,
"name": "Alice Smith",
"phone_number": "+14155552671",
"phone_verified": false,
"avatar_url": null,
"role": "member",
"disabled": false,
"external_id": null,
"public_metadata": {},
"private_metadata": {},
"created_at": "2024-01-10T09:00:00Z",
"updated_at": "2024-01-15T10:30:00Z",
"last_active_at": "2024-01-20T08:15:00Z"
}Get the Current User
From client-side (uses the session token):
curl "https://api.hellojohn.dev/v1/auth/me" \
-H "Authorization: Bearer eyJhbGciOiJFZERTQSJ9..."import { useUser } from "@hellojohn/react";
function Profile() {
const { user, isLoaded } = useUser();
if (!isLoaded) return <p>Loading...</p>;
return (
<div>
<p>{user.name}</p>
<p>{user.email}</p>
</div>
);
}List Users
curl "https://api.hellojohn.dev/v1/users" \
-H "Authorization: Bearer sk_live_abc123" \
-H "X-Tenant-ID: tnt_01HABCDEF654321"Query parameters:
| Parameter | Type | Description |
|---|---|---|
q | string | Search across email, name, phone |
role | string | Filter by role |
email_verified | boolean | Filter by verification status |
disabled | boolean | Include disabled users |
created_after | ISO date | Created after this date |
created_before | ISO date | Created before this date |
order_by | string | created_at (default), last_active_at, email |
order | string | asc or desc (default) |
limit | integer | Default 20, max 100 |
cursor | string | Pagination cursor |
Response:
{
"data": [
{
"id": "usr_01HABCDEF123456",
"email": "alice@example.com",
"name": "Alice Smith",
"role": "member",
"created_at": "2024-01-10T09:00:00Z"
}
],
"total": 842,
"next_cursor": "cur_01HABCDEF..."
}Advanced Search
Use POST /v1/admin/users/search for more complex queries:
curl -X POST "https://api.hellojohn.dev/v1/admin/users/search" \
-H "Authorization: Bearer sk_live_abc123" \
-H "X-Tenant-ID: tnt_01HABCDEF654321" \
-H "Content-Type: application/json" \
-d '{
"query": "alice",
"filters": {
"mfa_enrolled": true,
"created_after": "2024-01-01T00:00:00Z",
"organization_id": "org_01HABCDEF777666"
},
"sort": {"field": "last_active_at", "direction": "desc"},
"limit": 50
}'Paginating Results
HelloJohn uses cursor-based pagination. Fetch all users:
async function getAllUsers() {
const users = [];
let cursor: string | null = null;
do {
const params = new URLSearchParams({ limit: "100" });
if (cursor) params.set("cursor", cursor);
const res = await fetch(`/v1/users?${params}`, { headers: { ... } });
const page = await res.json();
users.push(...page.data);
cursor = page.next_cursor;
} while (cursor);
return users;
}Private Metadata Access
private_metadata is only returned when using a secret key. Client-side SDK responses never include it:
// Backend — private_metadata included
const user = await hj.users.get(userId); // returns private_metadata
// Frontend — private_metadata is null
const { user } = useUser(); // private_metadata always null