Service Identities
Production service identity creation, ownership, and lifecycle.
Namespace: Goodmem.Client.Api · Class: ServiceIdentitiesApi
Reach this surface as client.ServiceIdentities 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 service identity. |
GetAsync | Get a service identity. |
ListAsync | List service identities. |
UpdateAsync | Update a service identity. |
DeleteAsync | Delete a service identity. |
TransferOwnershipAsync | Transfer service-identity ownership. |
CreateAsync
Creates one production-workload identity owned by the authenticated human. No API key, role, grant, or authentication mapping is created implicitly.
Declaration
public Task<ServiceIdentityResponse> CreateAsync(CreateServiceIdentityRequest request, CancellationToken ct = default)
HTTP — POST /v1/service-identities
Parameters
| Type | Name | Description |
|---|---|---|
CreateServiceIdentityRequest | 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<ServiceIdentityResponse> — an awaitable that resolves to ServiceIdentityResponse.
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 identity = await client.ServiceIdentities.CreateAsync(
new CreateServiceIdentityRequest
{
DisplayName = "production-indexer",
Description = "Indexes newly uploaded memories",
Labels = new Dictionary<string, string> { ["environment"] = "production" },
}
);GetAsync
Returns a service identity after applying READ_SERVICE_IDENTITY authority. includeDeleted permits an authorized caller to inspect a permanent tombstone; it does not grant additional authority.
Declaration
public Task<ServiceIdentityResponse> GetAsync(string id, ServiceIdentitiesGetOptions? options = null, CancellationToken ct = default)
HTTP — GET /v1/service-identities/{id}
Parameters
| Type | Name | Description |
|---|---|---|
string | id | Service-identity UUID |
ServiceIdentitiesGetOptions | 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<ServiceIdentityResponse> — an awaitable that resolves to ServiceIdentityResponse.
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
var fetchedIdentity = await client.ServiceIdentities.GetAsync("your-service-identity-id");ListAsync
Requires LIST_SERVICE_IDENTITY on the GoodMem instance and READ_SERVICE_IDENTITY on each returned row. Owner and label filters, lifecycle filtering, authorization, and keyset pagination execute in PostgreSQL. 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<ServiceIdentityResponse> ListAsync(ServiceIdentitiesListOptions? options = null, CancellationToken ct = default)
HTTP — GET /v1/service-identities
Parameters
| Type | Name | Description |
|---|---|---|
ServiceIdentitiesListOptions | 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<ServiceIdentityResponse> — an async stream; await foreach yields each ServiceIdentityResponse 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 item in client.ServiceIdentities.ListAsync(
new ServiceIdentitiesListOptions { MaxResults = 25 }
)
)
Console.WriteLine($"{item.ServiceIdentityId} {item.DisplayName}");UpdateAsync
Updates only fields present in the request. Empty description clears that optional field. Ownership changes use the dedicated transfer endpoint.
Declaration
public Task<ServiceIdentityResponse> UpdateAsync(string id, UpdateServiceIdentityRequest request, CancellationToken ct = default)
HTTP — PUT /v1/service-identities/{id}
Parameters
| Type | Name | Description |
|---|---|---|
string | id | Service-identity UUID |
UpdateServiceIdentityRequest | 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<ServiceIdentityResponse> — an awaitable that resolves to ServiceIdentityResponse.
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 updatedIdentity = await client.ServiceIdentities.UpdateAsync(
"your-service-identity-id",
new UpdateServiceIdentityRequest
{
Description = "Indexes production knowledge sources",
}
);DeleteAsync
Permanently soft-deletes the principal. Its stored credentials remain audit records but can no longer authenticate because their subject is deleted. Repeating an authorized delete succeeds without rewriting audit data.
Declaration
public Task DeleteAsync(string id, CancellationToken ct = default)
HTTP — DELETE /v1/service-identities/{id}
Parameters
| Type | Name | Description |
|---|---|---|
string | id | Service-identity 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.ServiceIdentities.DeleteAsync("your-service-identity-id");TransferOwnershipAsync
Transfers administrative ownership to another active principal. A service identity cannot own itself. The service identity's subject, immutable creator, credentials, grants, and roles are unchanged.
Declaration
public Task<TransferServiceIdentityOwnershipResponse> TransferOwnershipAsync(string id, TransferOwnershipRequest request, CancellationToken ct = default)
HTTP — POST /v1/service-identities/{id}:transferOwnership
Parameters
| Type | Name | Description |
|---|---|---|
string | id | Service-identity UUID |
TransferOwnershipRequest | 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<TransferServiceIdentityOwnershipResponse> — an awaitable that resolves to TransferServiceIdentityOwnershipResponse.
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 transferredIdentity = await client.ServiceIdentities.TransferOwnershipAsync(
"your-service-identity-id",
new TransferOwnershipRequest
{
NewOwnerId = "your-new-owner-principal-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.
CreateServiceIdentityRequest
Creates a service identity owned by the authenticated human. It does not create credentials, roles, or grants.
| Property | Type | JSON (wire) | Description |
|---|---|---|---|
ServiceIdentityId | string | serviceIdentityId | Optional client-provided UUID; generated by the server when omitted. (optional) |
DisplayName | string | displayName | Globally unique, nonblank operator-facing name. |
Description | string | description | Optional operator description. (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) |
ServiceIdentityResponse
A durable production workload identity. Credentials, grants, and roles are separate resources and are not included.
| Property | Type | JSON (wire) | Description |
|---|---|---|---|
ServiceIdentityId | string | serviceIdentityId | OUTPUT_ONLY; immutable service-identity UUID. |
DisplayName | string | displayName | OUTPUT_ONLY; unique operator-facing display name. |
Description | string | description | OUTPUT_ONLY; optional operator description. (optional) |
OwnerPrincipalId | string | ownerPrincipalId | OUTPUT_ONLY; current administrative owner principal UUID. |
CreatorPrincipalId | string | creatorPrincipalId | OUTPUT_ONLY; immutable HUMAN creator-principal UUID. |
Labels | IReadOnlyDictionary<string, string> | labels | OUTPUT_ONLY; mutable labels. At most 20 entries; keys and values contain at most 255 characters; keys use [a-z0-9._-]. |
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. |
ListServiceIdentitiesResponse
One authorization-filtered page of service identities.
| Property | Type | JSON (wire) | Description |
|---|---|---|---|
ServiceIdentities | IReadOnlyList<ServiceIdentityResponse> | serviceIdentities | OUTPUT_ONLY; service identities in stable keyset order. |
NextToken | string | nextToken | OUTPUT_ONLY; opaque continuation token, omitted after the final page. (optional) |
TransferServiceIdentityOwnershipResponse
The service identity after its administrative ownership transfer.
| Property | Type | JSON (wire) | Description |
|---|---|---|---|
ServiceIdentity | ServiceIdentityResponse | serviceIdentity | OUTPUT_ONLY; updated service identity. |
UpdateServiceIdentityRequest
Updates explicitly present profile fields. Empty description clears it; omitted fields remain unchanged. Ownership is changed only through the transfer endpoint.
| Property | Type | JSON (wire) | Description |
|---|---|---|---|
DisplayName | string | displayName | Replacement display name. A present blank value is invalid. (optional) |
Description | string | description | Replacement description. An empty string clears the description. (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) |