Customer Management
AI skills for customer onboarding, customer lookup, and admin user management.
Overview
This skill enables AI agents and applications to onboard new customers, look up existing customers, and manage tenant admin users using Eclipse APIs.
It supports:
- Onboarding new customers (create record → trigger KYC → check eligibility → provision wallet)
- Looking up customers by phone, identity number, or name
- Creating and managing tenant admin portal users
All base URLs follow the pattern: {baseUrl}/eclipse-conductor/rest/v1/tenants/{tenantId}/... for tenant-scoped operations. Phone numbers must be digits only with no + prefix (e.g. 27821234567).
1. Customer Onboarding
Trigger: "onboard a new customer", "create a customer", "register a user"
Required inputs: tenantId, firstName, lastName, phone1, email, nationalIdentityNumber, userTypeId, externalUniqueId
New customer onboarding follows five steps: create the customer record, trigger KYC, confirm the result, check wallet eligibility, then provision the wallet. KYC must run before wallet provisioning — eligibility is determined by the KYC outcome.
Step 1 — Create Customer
POST {baseUrl}/eclipse-conductor/rest/v1/tenants/{tenantId}/customers
Authorization: Bearer {jwt}
Content-Type: application/json{
"firstName": "Sipho",
"lastName": "Dlamini",
"phone1": "27821234567",
"email": "[email protected]",
"nationalIdentityNumber": "9001015009087",
"userTypeId": 1,
"externalUniqueId": "cust-a1b2c3d4-e5f6-7890-abcd-ef1234567890",
"version": 0
}externalUniqueId is required — use a UUID unique to this customer. version: 0 is required for optimistic locking on creation.
Response:
{
"customerId": 3847291,
"firstName": "Sipho",
"lastName": "Dlamini",
"phone1": "27821234567",
"email": "[email protected]",
"nationalIdentityNumber": "9001015009087",
"status": "ACTIVE",
"userTypeId": 1,
"externalUniqueId": "cust-a1b2c3d4-e5f6-7890-abcd-ef1234567890",
"created": "2026-05-19T08:30:00.000Z"
}Step 2 — Trigger KYC
POST {baseUrl}/eclipse-conductor/rest/v1/tenants/{tenantId}/customers/{customerId}/ratify
Authorization: Bearer {jwt}
Content-Type: application/jsonRequest body may be empty ({}) to run the default check set, or include type (NORMAL or COMPARISON) and checksToRun to limit which checks are executed.
Response: An EclipseKycResult object containing one field per configured check (e.g. sanctionsListCheck, selfieMatchesNationalIdentity, nationalIdentityIsLegitimate), plus a top-level status string and lastModified timestamp.
{
"status": "PASSED",
"lastModified": "2026-05-19T08:30:20.000Z",
"sanctionsListCheck": { "result": "PASSED", "... per-check fields" },
"selfieMatchesNationalIdentity": { "result": "PASSED", "... per-check fields" }
}Step 3 — Check KYC Result
GET {baseUrl}/eclipse-conductor/rest/v1/tenants/{tenantId}/customers/{customerId}/ratify?offset=0&limit=1
Authorization: Bearer {jwt}Returns a list of EclipseKycResult objects, most recent first.
Expected output: surface the top-level status field and any per-check results that are not PASSED. Poll every 30–60 seconds until status is no longer PENDING.
Step 4 — Check Wallet Eligibility
Before provisioning a wallet, confirm which wallet types the customer is eligible for. This is determined by the KYC outcome.
GET {baseUrl}/eclipse-conductor/rest/v1/tenants/{tenantId}/customers/{customerId}/wallet-types
Authorization: Bearer {jwt}Response:
[
{ "walletTypeId": 5001, "allowed": true },
{ "walletTypeId": 5002, "allowed": false }
]Only provision a wallet for a walletTypeId where allowed is true. Do not proceed if no eligible types are returned.
Step 5 — Create Wallet
POST {baseUrl}/eclipse-conductor/rest/v1/tenants/{tenantId}/customers/{customerId}/wallets
Authorization: Bearer {jwt}
Content-Type: application/json{
"walletTypeId": 5001,
"currency": "ZAR",
"status": "ACTIVE",
"externalUniqueId": "wal-a1b2c3d4-e5f6-7890-abcd-ef1234567890",
"version": 0
}status: "ACTIVE", externalUniqueId, and version: 0 are all required. Use a UUID distinct from the customer's externalUniqueId.
Response:
{
"walletId": 1092847,
"customerId": 3847291,
"walletTypeId": 5001,
"name": "Primary Wallet",
"currency": "ZAR",
"currentBalance": 0.00,
"availableBalance": 0.00,
"status": "ACTIVE",
"created": "2026-05-19T08:30:15.000Z"
}Notes
externalUniqueIdis required on both customer and wallet creation. Use a unique UUID for each. Duplicate values are rejected with409 Conflict.version: 0is required on both customer and wallet creation for optimistic locking.status: "ACTIVE"is required on wallet creation.- KYC must complete with
PASSEDbefore checking eligibility — do not skip steps 2–3. - KYC
statusvalues:PASSED,FAILED,PENDING. Individual check fields indicate which check failed.
API Reference: POST /customers · POST /customers/{id}/ratify · GET /customers/{id}/wallet-types · POST /customers/{id}/wallets
2. Customer Lookup
Trigger: "find customer", "look up user", "search for customer", "who is customer X"
Required inputs: At least one of: phone number, name, identity number, or externalId; plus tenantId
Step 1 — Search by Phone or Identity
GET {baseUrl}/eclipse-conductor/rest/v1/tenants/{tenantId}/customers?identity=27821234567&limit=10
Authorization: Bearer {jwt}Response:
[
{
"customerId": 3847291,
"firstName": "Sipho",
"lastName": "Dlamini",
"phone1": "27821234567",
"email": "[email protected]",
"nationalIdentityNumber": "9001015009087",
"status": "ACTIVE",
"created": "2026-05-19T08:30:00.000Z"
}
]Step 2 (Alternative) — Search by Name
GET {baseUrl}/eclipse-conductor/rest/v1/tenants/{tenantId}/customers?firstName=Sipho&lastName=Dlamini&limit=10
Authorization: Bearer {jwt}Step 3 — Get Full Customer Profile
GET {baseUrl}/eclipse-conductor/rest/v1/tenants/{tenantId}/customers/{customerId}
Authorization: Bearer {jwt}Response:
{
"customerId": 3847291,
"title": "MR",
"firstName": "Sipho",
"lastName": "Dlamini",
"phone1": "27821234567",
"email": "[email protected]",
"nationalIdentityNumber": "9001015009087",
"status": "ACTIVE",
"created": "2026-05-19T08:30:00.000Z",
"lastModified": "2026-05-19T08:30:00.000Z"
}Notes
- If multiple results are returned, list them and ask the user to confirm which customer they mean.
- Pagination is supported via
limitandoffset. - The
identityquery parameter matches against phone number or identity number.
API Reference: GET /customers · GET /customers/{customerId}
3. Admin User Management
Trigger: "create admin user", "add operator user", "new portal user"
Required inputs: tenantId, firstName, lastName, email, phone1, identity, role(s)
Step 1 — Create Admin User
POST {baseUrl}/eclipse-conductor/rest/v1/tenants/{tenantId}/admin-users
Authorization: Bearer {jwt}
Content-Type: application/json{
"firstName": "Thembi",
"lastName": "Nkosi",
"email": "[email protected]",
"phone1": "27794567890",
"identity": "thembi.nkosi",
"password": "initialPassword123!",
"positions": ["LEVEL_01"]
}Response:
{
"adminUserId": 58291,
"firstName": "Thembi",
"lastName": "Nkosi",
"email": "[email protected]",
"phone1": "27794567890",
"identity": "thembi.nkosi",
"positions": ["LEVEL_01"]
}Step 2 — List Admin Users
GET {baseUrl}/eclipse-conductor/rest/v1/tenants/{tenantId}/admin-users
Authorization: Bearer {jwt}Notes
identity(the login username) must be unique across the platform. Using email as identity is a common convention.passwordis required on creation; it may be passed as cleartext (Eclipse will hash it) or pre-hashed with BCrypt.positionscontrols access level. Valid values:TENANT_SYSTEM,LEVEL_01–LEVEL_20,ONBOARDING. The specific permissions attached to each level are configured per tenant.- Phone number must be digits only with no
+prefix (e.g.27794567890).
API Reference: POST /admin-users · GET /admin-users
4. Customer Self-Registration (ONBOARDING Pattern)
Trigger: "build a customer onboarding PWA", "self-service registration", "customer signs themselves up", "onboarding flow for a mobile app"
Required inputs: tenantId, ONBOARDING admin user credentials (identity + password), baseUrl
This pattern bootstraps customer-direct API access. An admin user with the ONBOARDING position acts as a semi-public bootstrap credential embedded in the app. It creates the customer record and sets their login credentials, then immediately hands off to a customer-owned JWT. The ONBOARDING JWT is never used again once the customer has authenticated.
NoteThe ONBOARDING admin user must be created by a tenant administrator before this flow can run. See Admin User Management — set
"positions": ["ONBOARDING"]. The ONBOARDING position is hard-coded to four permissions only:ProfileOfNewCustomer.CREATE.Allowed,ProfileOfNewCustomer.UPDATE.Allowed,ProfileOfNewCustomer.READ.Allowed, andAttachment.CREATE.Allowed. It cannot be granted any other permissions.
Step 1 — Obtain ONBOARDING JWT
Log in as the ONBOARDING admin user to get a bootstrap JWT.
POST {baseUrl}/eclipse-conductor/rest/v1/authentication/login
Content-Type: application/json{
"identity": "onboarding-user",
"password": "onboardingPassword"
}Response (200 OK):
{
"headerName": "Authorization",
"headerValue": "Bearer eyJhbGci...",
"expiresEpochSecs": 1774277221
}Use headerValue as the Authorization header for steps 2–4. Do not store the ONBOARDING credentials in client-side JavaScript — inject them via a backend proxy or environment variable at build time.
Step 2 — Check for Duplicate Customer
Before creating a record, confirm no customer with the same phone number or identity already exists.
HEAD {baseUrl}/eclipse-conductor/rest/v1/tenants/{tenantId}/customers?phone1=27821234567
Authorization: Bearer {onboardingJwt}| Response | Meaning |
|---|---|
200 OK | Customer already exists — prompt user to log in instead |
404 Not Found | No existing customer — proceed to Step 3 |
Supported query parameters: phone1, email, identity, nationalIdentityNumber, passportNumber, asylumRefNumber. At least one is required.
Step 3 — Create Customer Record
POST {baseUrl}/eclipse-conductor/rest/v1/tenants/{tenantId}/customers
Authorization: Bearer {onboardingJwt}
Content-Type: application/json{
"firstName": "Sipho",
"lastName": "Dlamini",
"phone1": "27821234567",
"email": "[email protected]",
"nationalIdentityNumber": "9001015009087",
"userTypeId": 1,
"externalUniqueId": "cust-a1b2c3d4-e5f6-7890-abcd-ef1234567890",
"version": 0
}externalUniqueId must be a unique UUID. version: 0 is required for optimistic locking. phone1 must be digits only with no + prefix.
Response (200 OK):
{
"customerId": 3847291,
"firstName": "Sipho",
"lastName": "Dlamini",
"phone1": "27821234567",
"status": "ACTIVE"
}Save customerId — it is required for step 4.
Step 4 — Create Customer Identity (Login Credentials)
This is the critical step. Once a password identity exists on the customer, the ONBOARDING JWT can no longer modify their profile — the access gate is based on whether the customer has zero identities.
POST {baseUrl}/eclipse-conductor/rest/v1/tenants/{tenantId}/customers/{customerId}/identities
Authorization: Bearer {onboardingJwt}
Content-Type: application/json{
"identity": "sipho.dlamini",
"password": "SecureP@ssw0rd123"
}| Field | Type | Required | Description |
|---|---|---|---|
identity | string | Yes | Username for authentication. 3–40 characters |
password | string | Yes | Cleartext or BCrypt pre-hashed. 3–80 characters |
totpEnabled | boolean | No | Set true to require TOTP on login |
base64EncodedPublicKey | string | No | RSA public key for PKI-based authentication |
Response (200 OK):
{
"identity": "sipho.dlamini",
"userId": 3847291
}Step 5 — Log In as Customer and Obtain Customer JWT
Discard the ONBOARDING JWT. Authenticate as the newly created customer.
POST {baseUrl}/eclipse-conductor/rest/v1/authentication/login
Content-Type: application/json{
"identity": "sipho.dlamini",
"password": "SecureP@ssw0rd123"
}Response (200 OK):
{
"headerName": "Authorization",
"headerValue": "Bearer eyJhbGci...",
"expiresEpochSecs": 1774277221
}The headerValue is the customer JWT. Use it for all subsequent API calls — wallet creation, KYC, card operations, etc.
Notes
- ONBOARDING JWT is rate-limited. The platform enforces a maximum of 60 mutating requests (POST, PUT, DELETE) per 180 seconds per IP address. A PWA must handle
429 Too Many Requestswith backoff. - Do not use ONBOARDING JWT after step 4. Once an identity exists on the customer, the ONBOARDING position cannot access their profile. All further operations must use the customer JWT from step 5.
- ONBOARDING does not run KYC. If your flow requires KYC before wallet provisioning, trigger ratification after step 5 using the customer JWT or an operator JWT. See Customer Onboarding steps 2–4.
- Check for existing identities before re-running step 4. Use
HEAD {baseUrl}/.../customers/{customerId}/identities(returns200if identities exist,404if none) to avoid a409 Conflict. - ONBOARDING credentials must not be exposed client-side. Treat them as a semi-public secret — any user of the channel can retrieve them if embedded in JavaScript. Use a backend token-exchange endpoint to return the ONBOARDING JWT to the frontend without exposing the underlying credentials.
API Reference: HEAD /customers · POST /customers · POST /customers/{id}/identities · POST /authentication/login
Common Patterns for AI Agents
1. Full Onboarding Flow
- Create customer record (include
externalUniqueIdandversion: 0) - Trigger KYC ratification
- Poll KYC status every 30–60 seconds until
PASSEDorFAILED - If
FAILED, surface the specific failing check(s) for operator review — do not proceed to wallet creation - Check wallet eligibility (
GET .../wallet-types) — confirm at least oneallowed: truetype - Create wallet (include
status: "ACTIVE",externalUniqueId, andversion: 0)
2. Customer Lookup Before Action
Always look up the customer before performing operations:
- Search by phone or identity number
- Confirm identity with operator if multiple results are returned
- Retrieve full profile to confirm status is
ACTIVE
3. Onboarding Checklist
Before completing onboarding, verify:
- Customer record created (
customerIdreturned) - KYC status is
PASSED(notPENDINGorFAILED) - At least one wallet type returned with
allowed: truefrom the eligibility check - Wallet created (
walletIdreturned) - If KYC is
PENDING, advise the operator to check back in 30–60 seconds before proceeding
Error Handling
[
{
"type": "BUSINESS",
"severity": "LOW",
"description": "Customer already exists with this phone number",
"code": "USR004",
"traceId": "3a4b5c6d7e8f9a0b1c2d3e4f5a6b7c8d",
"spanId": "1a2b3c4d5e6f7a8b",
"environment": "eclipse-sandbox"
}
]| HTTP Status | Meaning | Common Cause |
|---|---|---|
400 | Bad request | Missing required field, invalid phone format |
401 | Unauthorised | JWT missing, expired, or malformed |
403 | Forbidden | Caller role does not have permission for this operation |
404 | Not found | Customer ID does not exist or belongs to a different tenant |
409 | Conflict | Customer with this phone number or identity already exists |
When a traceId is present in an error response, always surface it to the user — it is the primary identifier for diagnosing the failure.
Best Practices
- Include
Authorization: Bearer {jwt}on every request. Renew proactively if within 5 minutes of expiry. - Always confirm with the operator before re-triggering KYC on a customer with
PASSEDstatus — this restarts the verification process. - Apply
limitandoffsetpagination on all list endpoints. Default tolimit=20. - Mask sensitive data in output — never log or display full identity numbers.
- When a
traceIdis returned in an error, record it and reference it in any support escalation. - Tenant-scoped endpoints enforce tenant isolation — never pass a
tenantIdthat does not match the authenticated operator's context.
