Skip to content

GenAuth management SDK ​

This page explains how to manage users, organizations and permissions through qoni.genauth, including management credentials, pagination, responses and errors.

Unreleased interface

This page describes the GenAuth management extension to the Qoni SDK. As of 2026-10-09, the npm release @qoniai/qoni@0.9.0 does not include these new interfaces. The examples require an SDK build containing the management extension. The installation version will be updated when a release is available. See SDK reference for released delegation interfaces.

The GenAuth management contract uses Authing Node SDK 4.0.2 as its baseline, exposing 35 namespaces and 591 JSON management method names. Of these, 496 come from the official SDK and 95 are GenAuth extensions or compatibility names. Multiple names may map to the same endpoint. Deployment versions, management permissions and feature gates determine availability.

Requirements and initialization ​

Use Qoni AK/SK on a trusted Node.js server. Confirm that the credential-bound GenAuth user pool enables the management capabilities you need.

ts
import { Qoni } from '@qoniai/qoni'

const qoni = new Qoni({
  accessKey: process.env.QONI_ACCESS_KEY!,
  secretKey: process.env.QONI_SECRET_KEY!,
})

const { data: users } = await qoni.genauth.users.list({ page: 1, limit: 20 })
console.log(users.list, users.totalCount)

The SDK discovers GenAuth through Qoni runtime configuration, exchanges and caches a management token, and shares token exchanges between concurrent calls. Management calls use the AK/SK-bound user pool. They do not require a user access token, delegation token or QONI_USER_ID.

OptionTypeDescription
accessKey / secretKeystringQoni management credentials. Use only on a trusted server
hoststringOptional. Qoni Console / SDK gateway for a private deployment
genauthHoststringOptional. Explicit GenAuth address when it differs from runtime discovery
timeoutMsnumberTimeout per HTTP request; defaults to 30000
fetchtypeof fetchOptional. Custom transport

AK/SK here means Qoni credentials. When migrating Authing code, do not substitute native Authing management credentials for Qoni AK/SK.

Calling methods ​

Use a domain method or keep the Authing method name. Both call the same management endpoint:

ts
await qoni.genauth.groups.create({ code: 'engineering', name: 'Engineering' })

// Equivalent call; execute only one when creating the same group
// await qoni.genauth.management.createGroup({ code: 'engineering', name: 'Engineering' })

await qoni.genauth.groups.addMembers({
  code: 'engineering',
  userIds: ['<genauth-user-id>'],
})

All methods accept one input object. Authing positional parameters become named fields, such as management.deleteById({ id: '<id>' }). The SDK handles URL encoding and path binding. Inputs and results have TypeScript types; see the management method index for all methods and type names.

ParameterTypeDescription
First argumentMethod-specific Input typeBusiness input object; may be omitted when no fields are required
Second argument signalAbortSignalOptional. Pass { signal } to cancel a request
adminToken / userPoolIdstringOptional. Supply both to override exchanged management credentials

users.* ​

Create, read and update ​

ts
const { data: user } = await qoni.genauth.users.create({
  username: '<unique-username>',
  name: 'SDK Demo',
})

const { data: saved } = await qoni.genauth.users.get({ userId: user.userId })
await qoni.genauth.users.update({ userId: saved.userId, nickname: 'Demo' })
MethodMain inputdata
listpage, limit, keywords, options{ list, totalCount }
getuserIdUser object
getBatchuserIdsUser array
createUser fields such as username, email, nameUser object
createBatchlist; compatibility alias usersUser array
updateuserId and fields to updateUser object
deleteBatchuserIdsOperation result
iteratePagination and filtersAsyncIterable, yielding individual users

When both list and users are supplied to createBatch, list takes precedence. User pool settings determine username, email and phone uniqueness and creation rules.

An employed user cannot be deleted directly. Call users.resign({ userId }) only when departure is intended, then delete if required. The SDK never resigns a user automatically after deletion fails. Removing a user's last department may also be rejected by the server.

Pagination and iteration ​

users.list maps top-level page / limit to options.pagination, preserving other options. Top-level values take precedence when both pagination forms are supplied. management.listUsers uses the original Authing DTO's options.pagination. Other list methods use their own pagination parameters.

ts
const { data: page } = await qoni.genauth.users.list({ page: 1, limit: 20 })
console.log(page.totalCount)

for await (const user of qoni.genauth.users.iterate({ limit: 50 })) {
  console.log(user.userId)
  // break stops iteration without requesting another page
}
Fielditerate rule
pageStarting page; defaults to 1; must be a positive integer
limitPage size; defaults to 50; range 1–50
keywords / optionsThe same filters are preserved for every page
{ signal }Second argument; cancels a request or further iteration

Paginated lists are not transactional snapshots. User creation or deletion during iteration can affect page contents.

Keywords and advanced filters ​

ts
const { data: matches } = await qoni.genauth.users.list({
  keywords: 'alice',
  page: 1,
  limit: 20,
  options: { fuzzySearchOn: ['username', 'email'] },
})
console.log(matches.list)

Search requires a server index

Keyword and advanced-filter queries use the GenAuth search service. In representative live checks on 2026-10-08, unfiltered lists and reads by ID passed, but keyword and advanced-filter queries did not return newly created users. Custom-field keyword queries also returned an upstream error. Verify your deployment's search service and index before relying on these queries. The SDK preserves server results and errors rather than replacing empty results with a local scan.

Roles, resources and authorization ​

Roles and resources belong to an existing permission namespace. If the user pool has no default namespace, create and pass a business namespace explicitly. Keep namespace codes and application identifiers within 32 characters.

ts
await qoni.genauth.namespaces.create({ code: 'project', name: 'Project' })
await qoni.genauth.roles.create({
  namespace: 'project', code: 'reader', name: 'Reader',
})
await qoni.genauth.roles.assign({
  namespace: 'project',
  code: 'reader',
  targets: [{ targetType: 'USER', targetIdentifier: '<genauth-user-id>' }],
})
await qoni.genauth.resources.create({
  namespace: 'project',
  code: 'documents',
  type: 'API',
  apiIdentifier: '/documents',
  actions: [{ name: 'documents:read', description: 'Read documents' }],
})
await qoni.genauth.permissions.authorize({
  namespace: 'project',
  list: [{
    targetType: 'ROLE',
    targetIdentifiers: ['reader'],
    resources: [{
      code: 'documents:*', resourceType: 'API', actions: ['documents:read'],
    }],
  }],
})
const { data: permission } = await qoni.genauth.permissions.isAllowed({
  namespace: 'project',
  userId: '<genauth-user-id>',
  resource: 'documents:123',
  action: 'documents:read',
})
console.log(permission.allowed)
FieldUsed inDescription
namespaceRoles, resources, grants and checksShared business permission namespace
typeresources.createResource type; the example uses API
apiIdentifierAPI resource creationAPI identifier
resourceTypeResource items in permissions.authorizeGranted resource type
actionsResource definitions and grantsFull action name, such as documents:read
allowedisAllowed().dataServer permission decision

Resource definitions use type; grant items use resourceType. This example creates a role and grants permissions. Read existing business objects before reusing them to avoid duplicate creation.

Custom fields and data ​

Define a field before setting data on a target object:

ts
await qoni.genauth.customFields.set({
  list: [{
    targetType: 'USER', key: 'costCenter', label: 'Cost center', dataType: 'STRING',
  }],
})
await qoni.genauth.customFields.setData({
  targetType: 'USER',
  targetIdentifier: '<genauth-user-id>',
  list: [{ key: 'costCenter', value: 'engineering' }],
})
const { data: fields } = await qoni.genauth.customFields.list()
console.log(fields)
MethodPurposeMain parameters
listRead field definitionstargetType, defaults to USER
setCreate or update field definitionstargetType, key, label, dataType in list
setDataSet field values on an objecttargetType, targetIdentifier, list
getDataRead field values on an objecttargetType, targetIdentifier

Supply dataType when creating or updating a definition. An existing field's data type cannot change. Role field data also requires its namespace. Field lists with keyword depend on the search service; see the search limitation above.

Namespaces ​

All namespaces below belong to qoni.genauth. See the management method index for every method name, alias and type.

NamespacePurpose
usersUsers, identity bindings and lifecycle
rolesRoles and member assignments
groups / dynamicGroupsStatic and dynamic groups
organizations / departments / positionsOrganizations, departments and positions
applicationsApplication configuration and access grants
namespaces / resources / permissionsPermission namespaces, resources and permission checks
policies / dataPermissionsPolicies and data permissions
tenantsTenants, members and tenant settings
identityProvidersExternal identity providers and connections
customFieldsCustom field definitions and data
auditLogs / sessionsAudit logs and login sessions
security / whitelist / riskListsMFA, security settings, whitelists and risk lists
webhooks / eventsWebhooks and events
sync / pipelinesSynchronization and authentication flows
accessKeysAdministrator access credentials
publicAccounts / invitationsPublic accounts and invitations
metadata / email / billingMetadata, email and commercial configuration
devices / deviceRulesDevices and device rules
approvals / ldapApprovals and LDAP configuration
managementFlat entry point for all 591 method names

management is a flat entry point and is not counted among the 35 domain namespaces. Legacy GraphQL/v2, raw makeRequest, WebSocket helpers and multipart upload are outside this JSON management surface.

Responses, cancellation and errors ​

Management methods return QoniResponse<T>: business results are in data and request identifiers in meta. users.iterate yields individual user objects.

ts
import { QoniError } from '@qoniai/qoni'

const controller = new AbortController()
try {
  const { data, meta } = await qoni.genauth.roles.list(
    { namespace: 'project' },
    { signal: controller.signal },
  )
  console.log(data.list, meta.requestId)
} catch (error) {
  if (error instanceof QoniError) {
    console.error(error.status, error.code, error.requestId, error.message)
  }
  throw error
}

Call controller.abort() to cancel a request. If an exchanged management token expires or is rejected by authentication, the SDK refreshes and retries at most once. Permission errors, business errors, timeouts and network failures do not automatically replay writes.

ConditionSDK behaviorAction
Missing required fields or incomplete credential overrideQoniValidationErrorComplete the input
Insufficient management permissionsThrows a permission errorCheck administrator permissions and feature gates
HTTP 200 with business statusCode >= 400Throws QoniError rather than treating it as successCorrect the business input or state
Numeric apiCodePreserved as a string in error.codeRetain the code and request identifiers for diagnosis
Upstream or search service failureThrows the corresponding SDK error; original response in error.bodyCheck GenAuth; avoid blindly retrying writes

Explicit management token ​

If you already have a GenAuth management token, supply adminToken and userPoolId in the same call. They enter authentication headers rather than the business payload. The SDK does not refresh explicitly supplied tokens.

ts
const { data } = await qoni.genauth.roles.list({
  namespace: 'project',
  adminToken: process.env.GENAUTH_MANAGEMENT_TOKEN!,
  userPoolId: process.env.GENAUTH_USERPOOL_ID!,
})
console.log(data.list)

Passing only userPoolId does not switch the user pool for token exchange. Set genauthHost during initialization when the target GenAuth address differs from runtime discovery.

Next steps ​