Skip to content

Enrollment API (v3)

Mobile app subscriber enrollment flow — 8 steps for chit program applications.

Base URL

/v3/subscribers/enrollments

Authentication

All endpoints require a valid JWT bearer token for a SUBSCRIBER role user.

Authorization: Bearer <token>

Enrollment Lifecycle

An enrollment request is created from a chit program and belongs to the authenticated subscriber. The client keeps using the returned requestId for every later step.

The flow has two phases:

Phase Steps Expected request status Purpose
Pre-enrollment Basic info, addresses, income, documents INCOMPLETE until submit, then REQUESTED Collect KYC/profile details and submit the application for approval.
Payment Payment initiation approval/payment flow dependent Starts payment for the enrollment invoice through the payment pipeline.
Post-enrollment Nominee, PAN/income proof, agreement ENROLLED Completes details required after the subscriber has been enrolled.

Use GET /me to hydrate the enrollment screen and decide which sections already have saved data. Step submit endpoints are idempotent in the product sense: they update the current application slice when the request is still editable, and validation prevents edits after the application reaches a disallowed status.

Uploaded Assets

Some enrollment steps do not upload files directly. The mobile app should upload files through the existing file upload/document signing flows first, then call these endpoints to attach or copy the already stored assets.

Step Required pre-uploaded data
Documents Aadhaar front, Aadhaar back, and photograph on subscriber profile
PAN income PAN card and income proof on subscriber profile
Agreement Active signature asset owned by the subscriber

The signature asset APIs are available in v3 at:

/v3/subscribers/me/documents/signatures

Common Response Envelope

Every success response uses this shape:

{
  "status": "success", // "success" | "error" | "pending"
  "code": 200,
  "message": "...",
  "data": {/* endpoint-specific payload */}
}

Enums

Gender

MALE | FEMALE | OTHER

RelationType

FATHER | MOTHER | HUSBAND | WIFE

IncomeSource

PROFESSIONAL | AGRICULTURE | EMPLOYMENT | BUSINESS | OTHER

EmploymentType

FULL_TIME | PART_TIME | CONTRACT | SELF_EMPLOYED

EnrollmentStep

BASIC_INFO | ADDRESSES | INCOME | DOCUMENTS | SUBMITTED | NOMINEE | PAN_INCOME | AGREEMENT | COMPLETED


Endpoints


GET /me

Get the current subscriber's enrollment data across all steps.

Response 200

{
  "status": "success",
  "code": 200,
  "message": "Data fetched successfully",
  "data": {
    "BASIC_INFO": {
      "name": "Jane Doe" | null,
      "mobileNumber": "+919999999999" | null,
      "mobileNumberVerified": true | null,
      "countryCode": "IN" | null,
      "email": "jane@example.com" | null,
      "emailVerified": true | null,
      "dob": "1990-01-01" | null,
      "gender": "FEMALE" | null,
      "aadhaarNumber": "****-****-6789" | null,
      "pan": "ABCD****F" | null,
      "relationName": "John" | null,
      "relationType": "FATHER" | null
    },
    "ADDRESSES": {
      "presentAddress": { /* Address object */ } | null,
      "permanentAddress": { /* Address object */ } | null
    },
    "INCOME": {
      "monthlyIncome": "50000" | null,
      "incomeSource": "EMPLOYMENT" | null,
      "employmentDetails": {
        "employer": "ABC Corp" | null,
        "employmentType": "FULL_TIME" | null,
        "jobTitle": "Engineer" | null,
        "officeAddress": { /* Address object */ } | null
      } | null
    },
    "DOCUMENTS": {
      "aadhaarCardFrontUrl": "https://..." | null,
      "aadhaarCardBackUrl": "https://..." | null,
      "photographUrl": "https://..." | null
    },
    "NOMINEE": {
      "nominee": {
        "name": "John Doe" | null,
        "dob": "1995-06-15" | null,
        "relation": "Brother" | null,
        "mobileNumber": "+919999999998" | null,
        "countryCode": "IN" | null,
        "address": { /* Address object */ } | null
      }
    },
    "PAN_INCOME": {
      "panCardUrl": "https://..." | null,
      "incomeProofUrl": "https://..." | null
    },
    "AGREEMENT": {
      "signed": true | false
    }
  }
}

Address object

{
  "line1": "123 Main St" | null,
  "line2": "Apt 4B" | null,
  "pincode": "671310" | null,
  "postOffice": "Kasaragod" | null,
  "city": "Kasaragod" | null,
  "district": "Kasaragod" | null,
  "state": "Kerala" | null,
  "cityId": 12 | null,
  "postOfficeId": 12 | null
}

POST /programs/:programId/steps/basic-info

Step 1 — Submit basic personal information and start a new application or resume an incomplete one.

Params

Field Type Required Description
programId cuid2 string yes ID of the chit program to apply for

Body (all fields optional — missing values use existing subscriber data)

{
  "name": "Jane Doe", // string, 1–100 chars, Unicode letters/spaces/hyphens/apostrophes/periods
  "dob": "1990-01-01", // string (ISO date), age ≥ 18, must be in the past
  "gender": "FEMALE", // "MALE" | "FEMALE" | "OTHER"
  "aadhaarNumber": "523456789012", // string, 12 digits, must not start with 0 or 1
  "pan": "ABCDE1234F", // string, uppercase, pattern: 5 letters + 4 digits + 1 letter
  "relationType": "FATHER", // "FATHER" | "MOTHER" | "HUSBAND" | "WIFE"
  "relationName": "John Doe" // string, 1–100 chars
}

Response 200

{
  "status": "success",
  "code": 200,
  "message": "Basic info saved successfully",
  "data": {
    "requestId": "cuid2-string",
    "step": "BASIC_INFO"
  }
}

Errors

Code Message
400 This program doesn't exist or may have been removed.
400 You've already been invited. Please wait for the program to start.
400 You've already applied for this program.
400 Email has to be verified. Please verify your email and try again.
400 Mobile Number has to be verified. Please verify your mobile number and try again.
400 Missing required fields. Please fill in all required fields.

POST /:requestId/steps/addresses

Step 2 — Submit present and permanent addresses.

Params

Field Type Required Description
requestId cuid2 string yes Enrollment request ID

Body (all fields optional)

{
  "isPresentAddressSameAsPermanentAddress": false, // boolean (when true, permanentAddress is copied from presentAddress on the server)
  "presentAddress": {/* Address */},
  "permanentAddress": {/* Address */}
}

Address (all fields optional, at least line1 required in practice)

{
  "line1": "123 Main St", // string, 1–200 chars
  "line2": "Apt 4B", // string, 1–200 chars
  "pincode": "671310", // string, 6 digits
  "postOffice": "Kasaragod", // string, 1–100 chars
  "city": "Kasaragod", // string, 1–100 chars
  "district": "Kasaragod", // string, 1–100 chars
  "state": "Kerala", // string, 1–100 chars
  "cityId": 12, // number, positive integer
  "postOfficeId": 12 // number, positive integer
}

Response 200

{
  "status": "success",
  "code": 200,
  "message": "Addresses saved successfully",
  "data": {
    "requestId": "cuid2-string",
    "step": "ADDRESSES"
  }
}

Errors

Code Message
404 Request not found.
400 You've already applied for this program.
400 Please provide a valid address. (when line1 is missing and addresses are not null)

POST /:requestId/steps/income

Step 3 — Submit income details and employment information.

Params

Field Type Required Description
requestId cuid2 string yes Enrollment request ID

Body (all fields optional)

{
  "monthlyIncome": "50000", // string | number — converted to Prisma.Decimal
  "incomeSource": "EMPLOYMENT", // "PROFESSIONAL" | "AGRICULTURE" | "EMPLOYMENT" | "BUSINESS" | "OTHER"
  "employmentDetails": {
    "employer": "ABC Corp", // string, 1–100 chars
    "employmentType": "FULL_TIME", // "FULL_TIME" | "PART_TIME" | "CONTRACT" | "SELF_EMPLOYED"
    "jobTitle": "Engineer", // string, 1–100 chars
    "officeAddress": {/* Address */}
  }
}

Response 200

{
  "status": "success",
  "code": 200,
  "message": "Income details saved successfully",
  "data": {
    "requestId": "cuid2-string",
    "step": "INCOME"
  }
}

Errors

Code Message
404 Request not found.
400 You've already applied for this program.
400 Missing required employment details. (when incomeSource is EMPLOYMENT but employmentDetails is missing/incomplete)

POST /:requestId/steps/documents

Step 4 — Accept terms and submit the application (moves from INCOMPLETEREQUESTED).

Params

Field Type Required Description
requestId cuid2 string yes Enrollment request ID

Body

{
  "acceptedTermsAndConditions": true // must be exactly `true`; validation rejects any other value
}

Server-side injected fields: ipAddress (from req.ip), userAgent (from user-agent header).

Response 200

{
  "status": "success",
  "code": 200,
  "message": "Application submitted successfully",
  "data": {
    "requestId": "cuid2-string",
    "step": "SUBMITTED"
  }
}

Errors

Code Message
404 Request not found.
400 You've already applied for this program.
400 Almost there! Please accept the terms and conditions to continue.

POST /:requestId/payments

Step 5 — Initiate payment for an enrollment invoice.

Params

Field Type Required Description
requestId cuid2 string yes Enrollment request ID

Body

{
  "invoiceId": "cuid2-string", // required — invoice ID for the enrollment
  "idempotencyKey": "unique-key-1", // required — string, 1–255 chars, prevents duplicate payments
  "grossAmount": "5000.00", // required — string, at least 1 char
  "currency": "INR", // required — only "INR" is accepted
  "deviceId": "device-abc-123" // optional — string, 1–255 chars; falls back to idempotencyKey
}

Server-side injected fields: ipAddress (from req.ip), userAgent (from user-agent header).

Response 200

{
  "status": "success",
  "code": 200,
  "message": "Payment initiated successfully",
  "data": {
    "transactionId": "txn-cuid2",
    "paymentToken": "pg-token",
    "accessKey": "pg-access-key",
    "checkoutMode": "SDK" | "REDIRECT" | "IFRAME"
  }
}

Errors

Code Message
404 We couldn't find this application. Please check and try again.

POST /:requestId/cancel

Cancel an enrollment request. Allows cancelling from INCOMPLETE, REQUESTED, ADMIN_APPROVED, or COMPANY_APPROVED status.

Params

Field Type Required Description
requestId cuid2 string yes Enrollment request ID

Body

{
  "comment": "I am cancelling my enrollment" // required — string, trimmed, 1–500 chars
}

Response 200

{
  "status": "success",
  "code": 200,
  "message": "Enrollment cancelled successfully",
  "data": {
    "requestId": "cuid2-string"
  }
}

Errors

Code Message
404 Application request not found.
400 Cannot cancel application in "ENROLLED" status.

POST /:requestId/post-enrollment/nominee

Step 6 — Submit nominee details (post-enrollment, after payment is complete and status is ENROLLED).

Params

Field Type Required Description
requestId cuid2 string yes Enrollment request ID

Body

{
  "nominee": {
    "name": "John Doe", // string, 1–100 chars
    "relation": "Brother", // string
    "mobileNumber": "+919999999999", // string — preprocessed: trimmed, leading zeros removed, prepended with "+" (or kept if already present), must be 11–16 total chars
    "countryCode": "IN", // string, ISO 3166-1 alpha-2 (default: "IN")
    "dob": "1995-06-15", // string (ISO date)
    "address": {/* Address */}
  }
}

Response 200

{
  "status": "success",
  "code": 200,
  "message": "Nominee details saved successfully",
  "data": {
    "requestId": "cuid2-string",
    "step": "NOMINEE"
  }
}

Errors

Code Message
404 This application doesn't exist or may have been removed.
400 Enrollment is not yet complete. Complete payment first.
400 Nominee details are required.
400 We couldn't save the nominee details right now. Please try again.

POST /:requestId/post-enrollment/pan-income

Step 7 — Submit PAN card and income proof documents to the enrolled subscriber record (post-enrollment, status ENROLLED).

Documents must already be uploaded to the subscriber's profile via the file upload flow. This step copies the URLs from the subscriber record to the enrolled subscriber record.

Params

Field Type Required Description
requestId cuid2 string yes Enrollment request ID

Body: none.

Response 200

{
  "status": "success",
  "code": 200,
  "message": "Documents submitted successfully",
  "data": {
    "requestId": "cuid2-string",
    "step": "PAN_INCOME"
  }
}

Errors

Code Message
404 This application doesn't exist or may have been removed.
400 Enrollment is not yet complete. Complete payment first.
404 Subscriber not found.
400 Please upload all the required documents. (plus message, path, missing details)

POST /:requestId/post-enrollment/agreement

Step 8 — Sign the chit agreement (post-enrollment, status ENROLLED). Requires a pre-uploaded signature asset and acceptance of terms. Generates a blank monthly-chitty-agreement-en PDF; enrollment details will be filled in a later version.

Params

Field Type Required Description
requestId cuid2 string yes Enrollment request ID

Body

{
  "signatureAssetId": "cuid2-string", // required — signature asset ID from the document signing module
  "acceptedTermsAndConditions": true // must be exactly `true`
}

Response 200

{
  "status": "success",
  "code": 200,
  "message": "Agreement signed successfully",
  "data": {
    "requestId": "cuid2-string",
    "step": "AGREEMENT",
    "templateKey": "monthly-chitty-agreement-en",
    "fileName": "request-id-monthly-chitty-agreement-en.pdf",
    "fileKey": "generated-agreements/monthly-chitty-agreement-en/...",
    "fileUrl": "https://example.com/generated-agreement.pdf",
    "sizeBytes": 12345,
    "message": "Agreement PDF generated successfully."
  }
}

Errors

Code Message
400 Signature is required to sign the agreement.
400 Almost there! Please accept the terms and conditions to continue.
404 This application doesn't exist or may have been removed.
400 Enrollment is not yet complete. Complete payment first.
400 Signature asset not found or not active. Please upload a new signature.

Signature Asset APIs

These APIs let the mobile app upload and reuse subscriber signature images for agreement signing. They use the same authentication envelope as the enrollment APIs and require a SUBSCRIBER bearer token.

POST /me/documents/signatures/generate

Generate a signed upload URL for a subscriber signature image.

Body

{
  "fileName": "signature.png", // required
  "contentType": "image/png" // required — "image/png" | "image/jpeg"
}

Response 200

{
  "status": "success",
  "code": 200,
  "message": "Signature upload URL generated successfully.",
  "data": {
    "signatureAssetId": "cuid2-string",
    "signedUrl": "https://s3-signed-upload-url"
  }
}

Client flow: upload the image bytes to signedUrl using the returned content type, then call the confirm endpoint with signatureAssetId.

POST /me/documents/signatures/confirm

Confirm that the signature image was uploaded successfully. Confirmation marks the signature asset as ready and deactivates other signature assets owned by the subscriber.

Body

{
  "signatureAssetId": "cuid2-string"
}

Response 200

{
  "status": "success",
  "code": 200,
  "message": "Signature upload confirmed successfully.",
  "data": {
    "signatureAssetId": "cuid2-string",
    "fileUrl": "https://..."
  }
}

GET /me/documents/signatures

List existing signature assets for the authenticated subscriber. Use this to show previously uploaded signatures and pick an active/ready signatureAssetId for agreement signing.

Query

Field Type Required Description
pageNumber number no Page number, defaults normally.
pageSize number no Page size, defaults normally.

Response 200

{
  "status": "success",
  "code": 200,
  "message": "Signature assets fetched successfully.",
  "data": {
    "count": 1,
    "hasPreviousPage": false,
    "hasNextPage": false,
    "pageNumber": 1,
    "pageSize": 10,
    "totalPages": 1,
    "signatures": [
      {
        "id": "cuid2-string",
        "fileName": "signature.png",
        "contentType": "image/png",
        "fileUrl": "https://...",
        "status": "READY",
        "isActive": true,
        "uploadedAt": "2026-03-29T10:00:00.000Z",
        "createdAt": "2026-03-29T09:59:00.000Z",
        "updatedAt": "2026-03-29T10:00:00.000Z"
      }
    ]
  }
}

Use a ready, active signature asset ID as signatureAssetId in POST /:requestId/post-enrollment/agreement.


Flow Summary

Pre-enrollment (INCOMPLETE):
  1. BASIC_INFO ──→ 2. ADDRESSES ──→ 3. INCOME ──→ 4. DOCUMENTS + Terms
                                                REQUESTED (submitted for approval)
                                              (admin/company approval process)
                                                ENROLLED (payment complete)
Post-enrollment:
  5. Payment (initiate)
  6. NOMINEE ──→ 7. PAN_INCOME ──→ 8. AGREEMENT ──→ COMPLETED
  • Steps 1–4 transition from INCOMPLETEREQUESTED (step 4 is the submit action).
  • Steps 6–8 require status ENROLLED.
  • Step 5 (payment) is called during the admin approval window; it delegates to the existing payments pipeline.
  • Cancel is available at any pre-ENROLLED status (INCOMPLETE, REQUESTED, ADMIN_APPROVED, COMPANY_APPROVED).
  • The agreement step currently generates and stores a blank monthly-chitty-agreement-en PDF. The response includes storage metadata (fileKey, fileUrl, sizeBytes) instead of the older document-template submissionId.
  • Successful step responses return the saved requestId and logical step; detailed saved values should be refreshed from GET /me when the client needs the latest screen state.

Zod Validation Reference

Schema Field Zod Type Notes
CuidSchema IDs z.cuid2() cuid2 format
userNameSchema() names string → trim → NFC normalize → 1–100 chars → Unicode regex → optional titleCase letters, spaces, hyphens, apostrophes, periods only
AadhaarNoSchema aadhaar string → 12 digits, not starting with 0/1
PanSchema pan string → uppercase → /^[A-Z]{5}[0-9]{4}[A-Z]{1}$/
dateOfBirthSchema({ minAge: 18 }) dob string/Date → past date → age ≥ 18 → age ≤ 120
MobileNumberSchema mobile string → normalize → starts with + → digits only → 11–16 chars
CountryCodeSchema countryCode z.enum([...ISO 3166-1 alpha-2]) 249 country codes
PinCodeSchema pincode string → 6 digits, not starting with 0
IntegerIdSchema cityId/etc z.number().int().positive()
DecimalSchema monthlyIncome string/number/Prisma.Decimal → Prisma.Decimal preprocesses input