API reference / Onboarding
Onboarding API
Participant records for retailers and suppliers, payout destinations, integrations, API credentials, and webhook URLs.
PayAssure
Developer Dashboard
API reference / Onboarding
Participant records for retailers and suppliers, payout destinations, integrations, API credentials, and webhook URLs.
The controller prefix is /onbordings, including the existing spelling in the URL. The module exposes 13 routes.
| Method | Route | Authentication |
|---|---|---|
| POST | /onbordings | Public |
| POST | /onbordings/generate-keys | JWT bearer |
| GET | /onbordings/keys | JWT bearer |
| GET | /onbordings/integration/credentials | JWT bearer |
| GET | /onbordings | Public |
| GET | /onbordings/me | JWT bearer |
| GET | /onbordings/:id | JWT bearer |
| PATCH | /onbordings/payment | JWT bearer |
| PATCH | /onbordings/me | JWT bearer |
| PATCH | /onbordings/:id | JWT bearer |
| DELETE | /onbordings/:id | JWT bearer |
| PATCH | /onbordings/:id/webhook | JWT bearer |
| PATCH | /onbordings/payment/activate | JWT bearer |
Authorization: Bearer <JWT>
Content-Type: application/jsonThe bearer token is issued by POST /auth/login or POST /auth/refresh. Missing, expired, revoked, inactive, or invalid tokens return 401 Unauthorized.
participantType must be RETAILER or SUPPLIER. The service checks participantType, businessName, contactName, email, phoneNumber, settlementMethod, and settlementAccount. If every field is present and non-blank, status is DOCUMENTS_SUBMITTED; otherwise status is DRAFT.
Required:
participantType: RETAILER | SUPPLIER
businessName: string
Optional strings:
registrationNumber, kraPin, businessType, industry,
physicalAddress, contactName, phoneNumber,
settlementMethod, settlementAccount, posSystem,
settlementPreference
email: email address, used to link a user account
payment: objectMPESA:
{
"type": "MPESA",
"accountName": "Jane Doe",
"phoneNumber": "254712345678",
"provider": "Safaricom"
}
BANK:
{
"type": "BANK",
"accountName": "Jane Doe",
"bankCode": "07",
"accountNumber": "1234567890",
"shortcode": "123456",
"provider": "Example Bank"
}{
"statusCode": 400,
"message": [
"participantType must be a valid enum value",
"businessName must be a string"
],
"error": "Bad Request",
"path": "/onbordings"
}Unknown request-body properties are removed by the global whitelist validation pipe. Service exceptions use the normal NestJS shape, such as 404 Participant not found.
/onbordingsCreates a retailer or supplier onboarding record. This route is public; no JWT is required.
{
"participantType": "RETAILER",
"businessName": "ABC Supermarket",
"registrationNumber": "REG-1001",
"kraPin": "A123456789Z",
"businessType": "Limited Company",
"industry": "Retail",
"physicalAddress": "Nairobi, Kenya",
"contactName": "Jane Doe",
"email": "jane@example.com",
"phoneNumber": "+254700000000",
"settlementMethod": "BANK",
"settlementAccount": "123456789",
"posSystem": "Odoo",
"settlementPreference": "DAILY",
"payment": { "type": "BANK", "accountName": "Jane Doe", "bankCode": "07", "accountNumber": "1234567890", "shortcode": "123456", "provider": "Example Bank" }
}Incomplete profiles receive: Your onboarding request is currently in draft because the profile is incomplete. Please complete the required details to move it forward. A duplicate email and participant type returns 201 with: This onboarding request was not created because an onboarding record for the same participant type already exists for this user.
{
"message": "Onboarding created. Please register an account to complete your profile.",
"id": "participant-123",
"participantType": "RETAILER",
"businessName": "ABC Supermarket",
"businessType": "Limited Company",
"contactName": "Jane Doe",
"email": "jane@example.com",
"status": "DOCUMENTS_SUBMITTED",
"integration": null,
"payment": { "type": "BANK", "accountName": "Jane Doe", "bankCode": "07", "accountNumber": "1234567890", "shortcode": "123456", "provider": "Example Bank", "status": "PENDING_VERIFICATION", "isVerified": false, "paymentActivationSecret": "paysec_7f3c9a2b6d..." },
"createdAt": "2026-09-10T08:30:00.000Z",
"updatedAt": "2026-09-10T08:30:00.000Z"
}/onbordings/generate-keysGenerates credentials for the onboarding participant belonging to the email in the authenticated JWT. No participant ID is supplied.
Authorization: Bearer <JWT>
Content-Type: application/json
Request body: noneExisting credentials are not regenerated: API keys were not generated because they already exist. If an integration has missing credentials, they are regenerated with: API keys were generated and persisted because previous credentials were missing.
{
"message": "These are the API keys generated for the first time.",
"id": "participant-123",
"participantType": "RETAILER",
"businessName": "ABC Supermarket",
"status": "DOCUMENTS_SUBMITTED",
"integration": { "id": "integration-123", "merchantId": "pay_4bec11e5a382fe7c", "apiKey": "pk_live_d093937d634dcb700b6d34ba6f29c55e", "apiSecret": "sk_live_85faf3a09cb5b38cb84c48b09a67da9f", "environment": "production", "isActive": true, "createdAt": "2026-09-10T08:30:00.000Z" },
"payment": null,
"createdAt": "2026-09-10T08:30:00.000Z",
"updatedAt": "2026-09-10T08:30:00.000Z"
}/onbordings/keysReturns the latest integration credentials for the participant associated with the authenticated user's email.
Authorization: Bearer <JWT>
Content-Type: application/json
Request body: none{
"id": "participant-123",
"participantType": "RETAILER",
"businessName": "ABC Supermarket",
"status": "DOCUMENTS_SUBMITTED",
"integration": { "id": "integration-123", "merchantId": "pay_4bec11e5a382fe7c", "apiKey": "pk_live_...", "apiSecret": "sk_live_...", "environment": "production", "isActive": true, "createdAt": "2026-09-10T08:30:00.000Z" }
}/onbordings/integration/credentialsReturns the latest integration for a participant email. A JWT is required, but the requested email is not restricted to the authenticated user's email.
Authorization: Bearer <JWT>
Content-Type: application/json
GET /onbordings/integration/credentials?email=merchant@example.com&isActive=true
email: required email string
isActive: optional; true or 1 means true, any other supplied value means false{
"id": "integration-123",
"participantId": "participant-123",
"participantEmail": "merchant@example.com",
"merchantId": "pay_4bec11e5a382fe7c",
"apiKey": "pk_live_...",
"apiSecret": "sk_live_...",
"environment": "production",
"isActive": true,
"createdAt": "2026-09-10T08:30:00.000Z"
}/onbordingsLists participants publicly. No JWT is required.
Request body: nonePublic payment data omits activation-secret hashes, expiry timestamps, verification attempts, verification method, and verification timestamp. The current implementation still includes apiKey and apiSecret in the public integration object.
[
{
"id": "participant-123",
"participantType": "RETAILER",
"businessName": "ABC Supermarket",
"businessType": "Limited Company",
"contactName": "Jane Doe",
"email": "jane@example.com",
"status": "DOCUMENTS_SUBMITTED",
"integration": { "merchantId": "pay_...", "apiKey": "pk_live_...", "apiSecret": "sk_live_...", "environment": "production", "isActive": true },
"payment": { "type": "BANK", "accountName": "Jane Doe", "status": "VERIFIED", "isVerified": true, "provider": "Example Bank", "bankCode": "07", "accountNumber": "1234567890", "shortcode": "123456" },
"createdAt": "2026-09-10T08:30:00.000Z",
"updatedAt": "2026-09-10T08:30:00.000Z"
}
]/onbordings/meLooks up the participant using the email claim in the JWT.
Authorization: Bearer <JWT>
Content-Type: application/json
Request body: none{
"id": "participant-123",
"participantType": "RETAILER",
"businessName": "ABC Supermarket",
"businessType": "Limited Company",
"contactName": "Jane Doe",
"email": "jane@example.com",
"status": "DOCUMENTS_SUBMITTED",
"integration": null,
"payment": null,
"createdAt": "2026-09-10T08:30:00.000Z",
"updatedAt": "2026-09-10T08:30:00.000Z"
}/onbordings/:idReturns a participant and its latest integration. The route requires a JWT but does not apply an owner or role check to the requested ID.
Authorization: Bearer <JWT>
Content-Type: application/json
Path parameter: id = participant ID
Request body: none{
"id": "participant-123",
"participantType": "SUPPLIER",
"businessName": "Supplier Ltd",
"businessType": "Limited Company",
"contactName": "Jane Doe",
"email": "supplier@example.com",
"status": "DRAFT",
"integration": null,
"payment": null,
"createdAt": "2026-09-10T08:30:00.000Z",
"updatedAt": "2026-09-10T08:30:00.000Z"
}/onbordings/paymentUpdates the payout destination for the participant associated with the JWT email. This route does not accept a participant ID.
Authorization: Bearer <JWT>
Content-Type: application/json
{
"payment": { "type": "MPESA", "accountName": "Jane Doe", "phoneNumber": "254712345678", "provider": "Safaricom" }
}{
"id": "participant-123",
"participantType": "RETAILER",
"businessName": "ABC Supermarket",
"status": "DOCUMENTS_SUBMITTED",
"payment": { "type": "MPESA", "accountName": "Jane Doe", "phoneNumber": "254712345678", "provider": "Safaricom", "status": "PENDING_VERIFICATION", "isVerified": false, "paymentActivationSecret": "paysec_7f3c9a2b6d..." }
}/onbordings/mePartially updates the participant associated with the email claim in the JWT. No participant ID is required or accepted. Any subset of participant fields may be sent, including a nested payment object using the shared payment rules.
Authorization: Bearer <JWT>
Content-Type: application/json
{
"businessName": "ABC Supermarket Limited",
"contactName": "Jane Doe",
"phoneNumber": "+254700000000",
"settlementMethod": "BANK",
"settlementAccount": "123456789",
"payment": { "type": "BANK", "accountName": "Jane Doe", "bankCode": "07", "accountNumber": "1234567890" }
}Returns the updated OnboardingResponseDto. Status is recalculated to DRAFT or DOCUMENTS_SUBMITTED from the resulting profile fields./onbordings/:idPartially updates the participant identified by the path ID. The route requires a JWT, but the service does not enforce ownership or role-based authorization for the target ID.
Authorization: Bearer <JWT>
Content-Type: application/json
Path parameter: id = participant ID
{
"businessName": "ABC Supermarket Limited",
"contactName": "Jane Doe"
}Returns the updated OnboardingResponseDto. Status is recalculated to DRAFT or DOCUMENTS_SUBMITTED from the resulting profile fields./onbordings/:idDeletes the participant record. The controller requires a JWT, but the service does not enforce ownership or role-based authorization. Associated data follows database relationship delete behavior.
Authorization: Bearer <JWT>
Content-Type: application/json
Path parameter: id = participant ID
Request body: none{
"message": "Participant deleted successfully"
}/onbordings/:id/webhookUpdates the participant's webhook URL. The id path parameter identifies the participant.
Authorization: Bearer <JWT>
Content-Type: application/json
{
"webhookUrl": "https://merchant.example.com/payassure/webhook"
}Returns the updated OnboardingResponseDto./onbordings/payment/activateVerifies the pending payout destination using the activation secret generated when the payment destination was created or replaced. The participant is found from the JWT email; no participant ID is supplied.
Authorization: Bearer <JWT>
Content-Type: application/json
{
"paymentActivationSecret": "paysec_7f3c9a2b6d..."
}The response strips the stored activation-secret hash and expiry timestamp.
{
"id": "participant-123",
"participantType": "RETAILER",
"businessName": "ABC Supermarket",
"status": "DOCUMENTS_SUBMITTED",
"payment": { "type": "MPESA", "accountName": "Jane Doe", "phoneNumber": "254712345678", "provider": "Safaricom", "status": "VERIFIED", "isVerified": true, "verificationAttempts": 0, "verificationMethod": "PAYMENT_ACTIVATION_SECRET", "verifiedAt": "2026-09-10T09:00:00.000Z" }
}Most protected participant routes return an OnboardingResponseDto containing the participant, latest integration, and safe payment information. Stored payment security fields such as paymentActivationSecretHash, paymentActivationSecretExpiresAt, verificationAttempts, verificationMethod, and verifiedAt are normally removed, although activation may return verification details.