Users
.NET SDK reference for human users: enrollment management, creation, lookup, listing, updates, and deletion.
User lookup by id, email, or me.
Namespace: Goodmem.Client.Api · Class: UsersApi
Reach this surface as client.Users on a GoodmemClient. Every network method is asynchronous — it returns a Task<T> (or an IAsyncEnumerable<T> for pagination and streaming) and accepts a CancellationToken; the Async suffix marks the standard .NET Task-based async pattern.
Methods
| Method | Summary |
|---|---|
CreateAsync | Create a human user. |
GetAsync | Get a user by ID. |
ListAsync | List human users. |
UpdateAsync | Update a human user. |
DeleteAsync | Delete a human user. |
CreateEnrollmentAsync | Create a human-user enrollment. |
GetByUsernameAsync | Get user by username. |
GetEnrollmentAsync | Get a human-user enrollment. |
ListEnrollmentsAsync | List a human user's enrollments. |
MeAsync | Get current user profile. |
RevokeEnrollmentAsync | Revoke a human-user enrollment. |
CreateAsync
Creates one dormant human user after requiring instance-wide CREATE_USER authority. Creation does not issue a credential or create a role, grant, or authentication mapping.
Declaration
public Task<UserResponse> CreateAsync(CreateUserRequest request, CancellationToken ct = default)
HTTP — POST /v1/users
Parameters
| Type | Name | Description |
|---|---|---|
CreateUserRequest | request | The request payload; the linked model lists every field and its JSON wire name. |
CancellationToken | ct | Cancellation / deadline signal for the call. (optional) |
Returns
Task<UserResponse> — an awaitable that resolves to UserResponse.
Exceptions
| Type | Condition |
|---|---|
ArgumentNullException | request is null. |
NetworkException | The request could not reach the server (DNS, connection, or TLS failure). |
ApiException | The server returned a non-success (non-2xx) status. A status-specific subtype is thrown where it applies — e.g. NotFoundException (404), PermissionDeniedException (403), ConflictException (409). |
Example
var createdUser = await client.Users.CreateAsync(
new CreateUserRequest
{
Email = "[email protected]",
Username = "alex",
DisplayName = "Alex Example",
Labels = new Dictionary<string, string> { ["team"] = "search" },
}
);GetAsync
Retrieves a user by ID or email address. Exactly one of id and email must be provided.
Declaration
public Task<UserResponse> GetAsync(UsersGetOptions options, CancellationToken ct = default)
HTTP — GET /v1/users/{id}
Parameters
| Type | Name | Description |
|---|---|---|
UsersGetOptions | options | Options bag carrying the lookup key(s) / convenience knobs; the linked type lists them all. |
CancellationToken | ct | Cancellation / deadline signal for the call. (optional) |
Returns
Task<UserResponse> — an awaitable that resolves to UserResponse.
Exceptions
| Type | Condition |
|---|---|
ArgumentNullException | options is null. |
NetworkException | The request could not reach the server (DNS, connection, or TLS failure). |
ApiException | The server returned a non-success (non-2xx) status. A status-specific subtype is thrown where it applies — e.g. NotFoundException (404), PermissionDeniedException (403), ConflictException (409). |
Examples
Example 1:
var user = await client.Users.GetAsync(new UsersGetOptions { Id = "your-user-id" });
Console.WriteLine(user.Email);Example 2:
// Or look a user up by email instead of ID.
var user = await client.Users.GetAsync(
new UsersGetOptions { Email = "[email protected]" }
);
Console.WriteLine(user.UserId);ListAsync
Requires LIST_USER on the GoodMem instance and READ_USER on each returned row. Authorization, label filtering, lifecycle filtering, and keyset pagination run in PostgreSQL. includeDeleted expands the lifecycle view but grants no access. includeEnrollmentSummary requests non-secret bootstrap posture only on active rows where MANAGE_USER_ENROLLMENT is independently authorized.
LABEL FILTERS: Label filters accept either label.<key>=<value> or label[key]=value (for example, label.environment=production or label[environment]=production).
Declaration
public IAsyncEnumerable<UserResponse> ListAsync(UsersListOptions? options = null, CancellationToken ct = default)
HTTP — GET /v1/users
Parameters
| Type | Name | Description |
|---|---|---|
UsersListOptions | options | Options bag carrying the lookup key(s) / convenience knobs; the linked type lists them all. (optional) |
CancellationToken | ct | Cancellation / deadline signal for the call. (optional) |
Returns
IAsyncEnumerable<UserResponse> — an async stream; await foreach yields each UserResponse across pages / events.
Exceptions
| Type | Condition |
|---|---|
NetworkException | The request could not reach the server (DNS, connection, or TLS failure). |
ApiException | The server returned a non-success (non-2xx) status. A status-specific subtype is thrown where it applies — e.g. NotFoundException (404), PermissionDeniedException (403), ConflictException (409). |
Example
await foreach (var user in client.Users.ListAsync(new UsersListOptions { MaxResults = 25 }))
Console.WriteLine($"{user.UserId} {user.Email}");UpdateAsync
Updates only fields present in the request. Empty username or displayName values clear those optional fields. Updating a deleted user fails with 412.
Declaration
public Task<UserResponse> UpdateAsync(string id, UpdateUserRequest request, CancellationToken ct = default)
HTTP — PUT /v1/users/{id}
Parameters
| Type | Name | Description |
|---|---|---|
string | id | User UUID |
UpdateUserRequest | request | The request payload; the linked model lists every field and its JSON wire name. |
CancellationToken | ct | Cancellation / deadline signal for the call. (optional) |
Returns
Task<UserResponse> — an awaitable that resolves to UserResponse.
Exceptions
| Type | Condition |
|---|---|
ArgumentException | id is null or empty. |
ArgumentNullException | request is null. |
NetworkException | The request could not reach the server (DNS, connection, or TLS failure). |
ApiException | The server returned a non-success (non-2xx) status. A status-specific subtype is thrown where it applies — e.g. NotFoundException (404), PermissionDeniedException (403), ConflictException (409). |
Example
var updatedUser = await client.Users.UpdateAsync(
"your-user-id",
new UpdateUserRequest { DisplayName = "Alexandra Example" }
);DeleteAsync
Permanently soft-deletes the user and invalidates credentials acting for that subject. The GoodMem instance owner cannot be deleted; transfer ownership first. Repeating an authorized delete succeeds without rewriting audit data.
Declaration
public Task DeleteAsync(string id, CancellationToken ct = default)
HTTP — DELETE /v1/users/{id}
Parameters
| Type | Name | Description |
|---|---|---|
string | id | User UUID |
CancellationToken | ct | Cancellation / deadline signal for the call. (optional) |
Returns
Task — completes when the operation finishes; there is no response body.
Exceptions
| Type | Condition |
|---|---|
ArgumentException | id is null or empty. |
NetworkException | The request could not reach the server (DNS, connection, or TLS failure). |
ApiException | The server returned a non-success (non-2xx) status. A status-specific subtype is thrown where it applies — e.g. NotFoundException (404), PermissionDeniedException (403), ConflictException (409). |
Example
await client.Users.DeleteAsync("your-user-id");CreateEnrollmentAsync
Creates a short-lived, one-time enrollment credential for an existing dormant human. Requires MANAGE_USER_ENROLLMENT with ANY or EXACT authority on the target user. The raw credential is returned only once. rotateExisting atomically revokes and replaces a live enrollment; an expired enrollment is replaced automatically.
Declaration
public Task<CreateUserEnrollmentResponse> CreateEnrollmentAsync(string userId, CreateUserEnrollmentRequest? request = null, CancellationToken ct = default)
HTTP — POST /v1/users/{userId}/enrollments
Parameters
| Type | Name | Description |
|---|---|---|
string | userId | Human-user UUID |
CreateUserEnrollmentRequest | request | The request payload; the linked model lists every field and its JSON wire name. (optional) |
CancellationToken | ct | Cancellation / deadline signal for the call. (optional) |
Returns
Task<CreateUserEnrollmentResponse> — an awaitable that resolves to CreateUserEnrollmentResponse.
Exceptions
| Type | Condition |
|---|---|
ArgumentException | userId is null or empty. |
NetworkException | The request could not reach the server (DNS, connection, or TLS failure). |
ApiException | The server returned a non-success (non-2xx) status. A status-specific subtype is thrown where it applies — e.g. NotFoundException (404), PermissionDeniedException (403), ConflictException (409). |
Example
var createdEnrollment = await client.Users.CreateEnrollmentAsync(
"your-user-id",
new CreateUserEnrollmentRequest { RotateExisting = true }
);
// Save createdEnrollment.EnrollmentToken now; it is returned only once.GetByUsernameAsync
Returns a user selected by exact username after applying READ_USER authority. includeDeleted permits an authorized caller to inspect a permanent tombstone; it does not grant additional authority. Missing and unauthorized matches both return 404 so this guessable identifier cannot reveal whether a user exists.
Declaration
public Task<UserResponse> GetByUsernameAsync(string username, UsersGetByUsernameOptions? options = null, CancellationToken ct = default)
HTTP — GET /v1/users/username/{username}
Parameters
| Type | Name | Description |
|---|---|---|
string | username | Exact username |
UsersGetByUsernameOptions | options | Options bag carrying the lookup key(s) / convenience knobs; the linked type lists them all. (optional) |
CancellationToken | ct | Cancellation / deadline signal for the call. (optional) |
Returns
Task<UserResponse> — an awaitable that resolves to UserResponse.
Exceptions
| Type | Condition |
|---|---|
ArgumentException | username is null or empty. |
NetworkException | The request could not reach the server (DNS, connection, or TLS failure). |
ApiException | The server returned a non-success (non-2xx) status. A status-specific subtype is thrown where it applies — e.g. NotFoundException (404), PermissionDeniedException (403), ConflictException (409). |
Example
var userByUsername = await client.Users.GetByUsernameAsync("alex");
Console.WriteLine(userByUsername.Email);GetEnrollmentAsync
Returns non-secret metadata for one current or historical enrollment. The target user is resolved before the enrollment, and MANAGE_USER_ENROLLMENT with ANY or EXACT authority on that user is required. Raw enrollment tokens are never returned.
Declaration
public Task<UserEnrollmentResponse> GetEnrollmentAsync(string userId, string enrollmentId, CancellationToken ct = default)
HTTP — GET /v1/users/{userId}/enrollments/{enrollmentId}
Parameters
| Type | Name | Description |
|---|---|---|
string | userId | Human-user UUID |
string | enrollmentId | Enrollment UUID |
CancellationToken | ct | Cancellation / deadline signal for the call. (optional) |
Returns
Task<UserEnrollmentResponse> — an awaitable that resolves to UserEnrollmentResponse.
Exceptions
| Type | Condition |
|---|---|
ArgumentException | userId is null or empty. |
ArgumentException | enrollmentId is null or empty. |
NetworkException | The request could not reach the server (DNS, connection, or TLS failure). |
ApiException | The server returned a non-success (non-2xx) status. A status-specific subtype is thrown where it applies — e.g. NotFoundException (404), PermissionDeniedException (403), ConflictException (409). |
Example
var enrollment = await client.Users.GetEnrollmentAsync(
"your-user-id",
"your-enrollment-id"
);ListEnrollmentsAsync
Returns one newest-first page containing pending, expired, consumed, and revoked enrollment metadata. MANAGE_USER_ENROLLMENT with ANY or EXACT authority on the target user is required. The page never contains raw credentials.
Declaration
public IAsyncEnumerable<UserEnrollmentResponse> ListEnrollmentsAsync(string userId, UsersListEnrollmentsOptions? options = null, CancellationToken ct = default)
HTTP — GET /v1/users/{userId}/enrollments
Parameters
| Type | Name | Description |
|---|---|---|
string | userId | Human-user UUID |
UsersListEnrollmentsOptions | options | Options bag carrying the lookup key(s) / convenience knobs; the linked type lists them all. (optional) |
CancellationToken | ct | Cancellation / deadline signal for the call. (optional) |
Returns
IAsyncEnumerable<UserEnrollmentResponse> — an async stream; await foreach yields each UserEnrollmentResponse across pages / events.
Exceptions
| Type | Condition |
|---|---|
ArgumentException | userId is null or empty. |
NetworkException | The request could not reach the server (DNS, connection, or TLS failure). |
ApiException | The server returned a non-success (non-2xx) status. A status-specific subtype is thrown where it applies — e.g. NotFoundException (404), PermissionDeniedException (403), ConflictException (409). |
Example
await foreach (var item in client.Users.ListEnrollmentsAsync("your-user-id"))
Console.WriteLine($"{item.EnrollmentId} {item.Status}");MeAsync
Returns the human-user profile associated with the authenticated principal. Service principals do not have a human-user profile.
Declaration
public Task<UserResponse> MeAsync(CancellationToken ct = default)
HTTP — GET /v1/users/me
Parameters
| Type | Name | Description |
|---|---|---|
CancellationToken | ct | Cancellation / deadline signal for the call. (optional) |
Returns
Task<UserResponse> — an awaitable that resolves to UserResponse.
Exceptions
| Type | Condition |
|---|---|
NetworkException | The request could not reach the server (DNS, connection, or TLS failure). |
ApiException | The server returned a non-success (non-2xx) status. A status-specific subtype is thrown where it applies — e.g. NotFoundException (404), PermissionDeniedException (403), ConflictException (409). |
Example
var user = await client.Users.MeAsync();
Console.WriteLine($"{user.Email} {user.UserId}");RevokeEnrollmentAsync
Permanently revokes one outstanding enrollment after requiring MANAGE_USER_ENROLLMENT on its target user. Repeating an authorized revocation succeeds without replacing its original audit provenance. Consumed enrollments cannot be revoked.
Declaration
public Task RevokeEnrollmentAsync(string userId, string enrollmentId, CancellationToken ct = default)
HTTP — DELETE /v1/users/{userId}/enrollments/{enrollmentId}
Parameters
| Type | Name | Description |
|---|---|---|
string | userId | Human-user UUID |
string | enrollmentId | Enrollment UUID |
CancellationToken | ct | Cancellation / deadline signal for the call. (optional) |
Returns
Task — completes when the operation finishes; there is no response body.
Exceptions
| Type | Condition |
|---|---|
ArgumentException | userId is null or empty. |
ArgumentException | enrollmentId is null or empty. |
NetworkException | The request could not reach the server (DNS, connection, or TLS failure). |
ApiException | The server returned a non-success (non-2xx) status. A status-specific subtype is thrown where it applies — e.g. NotFoundException (404), PermissionDeniedException (403), ConflictException (409). |
Example
await client.Users.RevokeEnrollmentAsync("your-user-id", "your-enrollment-id");Data Models
Types in the Goodmem.Client.Models namespace. Each row lists the C# property, its type, the JSON wire name, and a description.
CreateUserRequest
Creates a human user without creating a credential, role assignment, grant, or external authentication mapping.
| Property | Type | JSON (wire) | Description |
|---|---|---|---|
UserId | string | userId | Optional client-provided user UUID; generated by the server when omitted. (optional) |
Email | string | email | Unique, nonempty email address. |
Username | string | username | Optional unique username. (optional) |
DisplayName | string | displayName | Optional human-facing display name. (optional) |
Labels | IReadOnlyDictionary<string, string> | labels | Optional labels for organization and filtering. At most 20 entries; keys and values contain at most 255 characters; keys use [a-z0-9._-]. (optional) |
UserResponse
A durable human user profile and its lifecycle metadata.
| Property | Type | JSON (wire) | Description |
|---|---|---|---|
UserId | string | userId | OUTPUT_ONLY; immutable user UUID. |
Email | string | email | OUTPUT_ONLY; unique email address. |
DisplayName | string | displayName | OUTPUT_ONLY; optional human-facing display name. (optional) |
Username | string | username | OUTPUT_ONLY; optional unique username. (optional) |
Labels | IReadOnlyDictionary<string, string> | labels | OUTPUT_ONLY; mutable labels attached to the user principal. At most 20 entries; keys and values contain at most 255 characters; keys use [a-z0-9._-]. |
EnrollmentSummary | UserEnrollmentSummary | enrollmentSummary | OUTPUT_ONLY; enrollment posture when requested and independently authorized. (optional) |
DeletedAt | DateTimeOffset | deletedAt | OUTPUT_ONLY; permanent deletion time in milliseconds, absent while active. (optional) |
DeletedById | string | deletedById | OUTPUT_ONLY; exact deleting actor UUID, absent while active. (optional) |
CreatedAt | DateTimeOffset | createdAt | OUTPUT_ONLY; creation time in milliseconds since the epoch. |
UpdatedAt | DateTimeOffset | updatedAt | OUTPUT_ONLY; most recent mutation time in milliseconds since the epoch. |
CreatedById | string | createdById | OUTPUT_ONLY; exact creating actor UUID. |
UpdatedById | string | updatedById | OUTPUT_ONLY; exact actor UUID for the most recent mutation. |
UserEnrollmentSummary
Non-secret enrollment posture for one active human user.
| Property | Type | JSON (wire) | Description |
|---|---|---|---|
BootstrapEligible | bool | bootstrapEligible | Whether no enrollment has been consumed and the user has no subject API key. |
OpenEnrollmentStatus | string | openEnrollmentStatus | State of the open enrollment, when one exists; present exactly with openEnrollmentExpiresAt. (optional) |
OpenEnrollmentExpiresAt | DateTimeOffset | openEnrollmentExpiresAt | Exclusive open-enrollment completion deadline in epoch milliseconds; present exactly with openEnrollmentStatus. (optional) |
CreateUserEnrollmentRequest
Options for issuing a one-time enrollment credential to the user named by the request path.
| Property | Type | JSON (wire) | Description |
|---|---|---|---|
EnrollmentId | string | enrollmentId | Optional client-provided enrollment UUID; generated when omitted. (optional) |
RotateExisting | bool | rotateExisting | Whether an existing live enrollment may be revoked and atomically replaced. (optional) |
CreateUserEnrollmentResponse
New enrollment metadata and its one-time raw enrollment credential.
| Property | Type | JSON (wire) | Description |
|---|---|---|---|
Enrollment | UserEnrollmentResponse | enrollment | OUTPUT_ONLY; non-secret metadata for the created enrollment. |
EnrollmentToken | string | enrollmentToken | OUTPUT_ONLY; one-time raw enrollment credential. Save and deliver it securely. |
UserEnrollmentResponse
Non-secret lifecycle metadata for a human user's one-time enrollment credential.
| Property | Type | JSON (wire) | Description |
|---|---|---|---|
EnrollmentId | string | enrollmentId | OUTPUT_ONLY; immutable enrollment UUID. |
UserId | string | userId | OUTPUT_ONLY; UUID of the human user invited to enroll. |
CredentialPrefix | string | credentialPrefix | OUTPUT_ONLY; non-secret prefix that identifies the enrollment credential safely. |
Status | string | status | OUTPUT_ONLY; current derived enrollment lifecycle state. |
ExpiresAt | DateTimeOffset | expiresAt | OUTPUT_ONLY; exclusive completion deadline in milliseconds since epoch. |
ConsumedApiKeyId | string | consumedApiKeyId | OUTPUT_ONLY; initial API-key UUID, present after consumption. (optional) |
CreatedAt | DateTimeOffset | createdAt | OUTPUT_ONLY; enrollment issuance time in milliseconds since epoch. |
ConsumedAt | DateTimeOffset | consumedAt | OUTPUT_ONLY; first-completion time, absent before consumption. (optional) |
RevokedAt | DateTimeOffset | revokedAt | OUTPUT_ONLY; permanent revocation time, absent while unrevoked. (optional) |
CreatedById | string | createdById | OUTPUT_ONLY; exact principal or API-key actor that issued the enrollment. |
RevokedById | string | revokedById | OUTPUT_ONLY; exact revoking actor UUID, absent while unrevoked. (optional) |
ListUsersResponse
One authorization-filtered page of human users.
| Property | Type | JSON (wire) | Description |
|---|---|---|---|
Users | IReadOnlyList<UserResponse> | users | OUTPUT_ONLY; users in stable keyset order. |
NextToken | string | nextToken | OUTPUT_ONLY; opaque continuation token, omitted after the final page. (optional) |
ListUserEnrollmentsResponse
One stable page of a human user's enrollment history.
| Property | Type | JSON (wire) | Description |
|---|---|---|---|
Enrollments | IReadOnlyList<UserEnrollmentResponse> | enrollments | OUTPUT_ONLY; enrollments ordered newest first. |
NextToken | string | nextToken | OUTPUT_ONLY; opaque continuation token, absent after the final page. (optional) |
UpdateUserRequest
Updates explicitly present profile fields. Empty username or displayName clears that optional field; an omitted field remains unchanged.
| Property | Type | JSON (wire) | Description |
|---|---|---|---|
Email | string | email | Replacement email. A present empty value is invalid. (optional) |
Username | string | username | Replacement username. An empty string clears the username. (optional) |
DisplayName | string | displayName | Replacement display name. An empty string clears the display name. (optional) |
ReplaceLabels | IReadOnlyDictionary<string, string> | replaceLabels | Complete replacement label map; an empty map clears all labels and is mutually exclusive with mergeLabels. At most 20 entries; keys and values contain at most 255 characters; keys use [a-z0-9._-]. (optional) |
MergeLabels | IReadOnlyDictionary<string, string> | mergeLabels | Labels to upsert; must contain at least one entry and is mutually exclusive with replaceLabels. The final stored map may contain at most 20 entries; keys and values contain at most 255 characters; keys use [a-z0-9._-]. (optional) |