PA

PayAssure

Developer Dashboard

Overview

The controller prefix is /onbordings, including the existing spelling in the URL. The module exposes 13 routes.

MethodRouteAuthentication
POST/onbordingsPublic
POST/onbordings/generate-keysJWT bearer
GET/onbordings/keysJWT bearer
GET/onbordings/integration/credentialsJWT bearer
GET/onbordingsPublic
GET/onbordings/meJWT bearer
GET/onbordings/:idJWT bearer
PATCH/onbordings/paymentJWT bearer
PATCH/onbordings/meJWT bearer
PATCH/onbordings/:idJWT bearer
DELETE/onbordings/:idJWT bearer
PATCH/onbordings/:id/webhookJWT bearer
PATCH/onbordings/payment/activateJWT bearer

Protected route headers

Authorization: Bearer <JWT>
Content-Type: application/json

The bearer token is issued by POST /auth/login or POST /auth/refresh. Missing, expired, revoked, inactive, or invalid tokens return 401 Unauthorized.

Participant and payment rules

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.

Participant fields

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: object

Payment payloads

MPESA:
{
  "type": "MPESA",
  "accountName": "Jane Doe",
  "phoneNumber": "254712345678",
  "provider": "Safaricom"
}

BANK:
{
  "type": "BANK",
  "accountName": "Jane Doe",
  "bankCode": "07",
  "accountNumber": "1234567890",
  "shortcode": "123456",
  "provider": "Example Bank"
}
  • MPESA: requires phoneNumber; legacy payerPhoneNumber is accepted and normalized. It rejects bankCode, accountNumber, and shortcode.
  • BANK: requires bankCode and accountNumber; it rejects phoneNumber and payerPhoneNumber. shortcode, when supplied, may contain digits only.
  • Backend-managed: clients must not supply isVerified, status, activation-secret fields, verification counters, or verification timestamps.
  • New or replaced payment destinations are stored as PENDING_VERIFICATION, receive a paysec_... activation secret, and expire after 24 hours.

Common validation error

{
  "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.

POST/onbordings

1. Create an onboarding participant

Creates a retailer or supplier onboarding record. This route is public; no JWT is required.

Request

{
  "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.

Success response

{
  "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"
}

Errors

  • 400 Bad Request: invalid enum, email, string, nested payment, or payment field.
  • 403 Forbidden: invalid payout destination rules, such as missing BANK fields, an MPESA bank field, or a supplied isVerified value.
  • 500 Internal Server Error: unexpected creation failure or invalid generated credential data.
POST/onbordings/generate-keys

2. Generate API keys

Generates credentials for the onboarding participant belonging to the email in the authenticated JWT. No participant ID is supplied.

Request

Authorization: Bearer <JWT>
Content-Type: application/json

Request body: none

Existing 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.

Success response

{
  "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"
}

Errors

  • 401 Unauthorized: missing or invalid JWT.
  • 404 Not Found: no onboarding participant exists for the authenticated email. Message: Onboarding participant not found for the authenticated user.
  • 500 Internal Server Error: generated credential data fails validation; no partial credentials are persisted.
GET/onbordings/keys

3. View the authenticated user's API keys

Returns the latest integration credentials for the participant associated with the authenticated user's email.

Request

Authorization: Bearer <JWT>
Content-Type: application/json

Request body: none

Success response

{
  "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" }
}

Errors

  • 401 Unauthorized: missing or invalid JWT.
  • 404 Not Found: participant is missing, or no integration with both API key and API secret exists. Message: API keys not found for the authenticated user.
GET/onbordings/integration/credentials

4. Look up integration credentials by email

Returns the latest integration for a participant email. A JWT is required, but the requested email is not restricted to the authenticated user's email.

Request

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

Success response

{
  "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"
}

Errors

  • 401 Unauthorized: missing or invalid JWT.
  • 404 Not Found: email is missing, or no matching integration exists. Message: Integration credentials not found for the provided email and active status.
GET/onbordings

5. List onboarding participants

Lists participants publicly. No JWT is required.

Request

Request body: none

Public 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.

Success response

[
  {
    "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"
  }
]

Errors

  • No controller-level errors are declared. An unexpected database failure may result in 500 Internal Server Error.
GET/onbordings/me

6. Get the authenticated user's participant

Looks up the participant using the email claim in the JWT.

Request

Authorization: Bearer <JWT>
Content-Type: application/json

Request body: none

Success response

{
  "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"
}

Errors

  • 401 Unauthorized: missing or invalid JWT, or token has no email claim. Message: Authenticated user email is required.
  • 404 Not Found: no participant matches the authenticated email. Message: Onboarding participant not found for the authenticated user.
GET/onbordings/:id

7. Get a participant by ID

Returns a participant and its latest integration. The route requires a JWT but does not apply an owner or role check to the requested ID.

Request

Authorization: Bearer <JWT>
Content-Type: application/json

Path parameter: id = participant ID
Request body: none

Success response

{
  "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"
}

Errors

  • 401 Unauthorized: missing or invalid JWT.
  • 404 Not Found: participant does not exist. Message: Participant not found.
PATCH/onbordings/payment

8. Update the authenticated user's payment destination

Updates the payout destination for the participant associated with the JWT email. This route does not accept a participant ID.

Request

Authorization: Bearer <JWT>
Content-Type: application/json

{
  "payment": { "type": "MPESA", "accountName": "Jane Doe", "phoneNumber": "254712345678", "provider": "Safaricom" }
}

Success response

{
  "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..." }
}

Errors

  • 400 Bad Request: wrapper or nested payment validation failed.
  • 401 Unauthorized: missing or invalid JWT.
  • 403 Forbidden: payment type or destination rules failed, including missing accountName, missing MPESA phoneNumber, or missing BANK bankCode/accountNumber.
  • 404 Not Found: authenticated user has no participant. Message: Onboarding participant not found for the authenticated user.
PATCH/onbordings/me

9. Update the authenticated user's participant

Partially 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.

Request

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" }
}

Success response

Returns the updated OnboardingResponseDto. Status is recalculated to DRAFT or DOCUMENTS_SUBMITTED from the resulting profile fields.

Errors

  • 400 Bad Request: invalid participant or nested payment fields.
  • 401 Unauthorized: missing or invalid JWT.
  • 401 Unauthorized: token has no email claim. Message: Authenticated user email is required.
  • 403 Forbidden: invalid payout destination rules.
  • 404 Not Found: no participant matches the authenticated user. Message: Onboarding participant not found for the authenticated user.
PATCH/onbordings/:id

10. Update a participant by ID

Partially 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.

Request

Authorization: Bearer <JWT>
Content-Type: application/json

Path parameter: id = participant ID

{
  "businessName": "ABC Supermarket Limited",
  "contactName": "Jane Doe"
}

Success response

Returns the updated OnboardingResponseDto. Status is recalculated to DRAFT or DOCUMENTS_SUBMITTED from the resulting profile fields.

Errors

  • 400 Bad Request: invalid participant or nested payment fields.
  • 401 Unauthorized: missing or invalid JWT.
  • 403 Forbidden: invalid payout destination rules.
  • 404 Not Found: participant does not exist. Message: Participant not found.
DELETE/onbordings/:id

11. Delete a participant

Deletes 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.

Request

Authorization: Bearer <JWT>
Content-Type: application/json

Path parameter: id = participant ID
Request body: none

Success response

{
  "message": "Participant deleted successfully"
}

Errors

  • 401 Unauthorized: missing or invalid JWT.
  • 404 Not Found: participant does not exist. Message: Participant not found.
PATCH/onbordings/:id/webhook

12. Update a participant webhook URL

Updates the participant's webhook URL. The id path parameter identifies the participant.

Request

Authorization: Bearer <JWT>
Content-Type: application/json

{
  "webhookUrl": "https://merchant.example.com/payassure/webhook"
}

Success response

Returns the updated OnboardingResponseDto.

Errors

  • 400 Bad Request: webhookUrl is missing or is not a valid URL.
  • 401 Unauthorized: missing or invalid JWT.
  • 404 Not Found: participant does not exist. Message: Participant not found.
PATCH/onbordings/payment/activate

13. Activate the authenticated user's payment destination

Verifies 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.

Request

Authorization: Bearer <JWT>
Content-Type: application/json

{
  "paymentActivationSecret": "paysec_7f3c9a2b6d..."
}

The response strips the stored activation-secret hash and expiry timestamp.

Success response

{
  "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" }
}

Errors

  • 400 Bad Request: activation secret is missing or is not a string.
  • 401 Unauthorized: missing or invalid JWT, or token has no email claim. Message: Authenticated user email is required.
  • 403 Forbidden: payment is not configured, is not pending verification, has no available secret, the secret has expired, or the secret is incorrect. Relevant messages include Participant payment destination is not configured, Participant payment destination is not pending verification, Payment activation secret is not available, Payment activation secret has expired, and Invalid payment activation secret.
  • 404 Not Found: no participant matches the authenticated email. Message: Onboarding participant not found for the authenticated user.

Response shapes and security

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.

Security considerations

  • API secrets are returned by key generation, key viewing, credential lookup, authenticated participant responses, and the public participant list. Protect these routes and consider removing secrets from public responses before production.
  • Payment activation secrets are returned in create/update payment responses. Deliver them only to the intended account owner and do not log them.
  • Credential lookup accepts any email from an authenticated caller.
  • Participant lookup, update, and deletion by ID require a JWT but currently do not enforce ownership or administrator authorization.