HelloJohn / docs
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:

ParameterTypeDescription
qstringSearch across email, name, phone
rolestringFilter by role
email_verifiedbooleanFilter by verification status
disabledbooleanInclude disabled users
created_afterISO dateCreated after this date
created_beforeISO dateCreated before this date
order_bystringcreated_at (default), last_active_at, email
orderstringasc or desc (default)
limitintegerDefault 20, max 100
cursorstringPagination 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..."
}

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

On this page