Skip to navigation

Back-End Integrations

Your back-end is the trusted boundary for Hosted Experience. It authenticates with Aarthik Labs, decides which borrower can start a journey, passes pre-fill data, and returns only embedURL to the browser or mobile app.

Do not call Aarthik Labs directly from your front-end.

Backend Responsibilities

Your back-end should:

  • store PLATFORM_API_KEY in server-side configuration only
  • validate the authenticated borrower in your own app
  • send a stable borrowerProviderID
  • send an explicit journeyType
  • pass only pre-fill data that your app is allowed to share
  • return only { "embedURL": "..." } to the front-end
  • avoid caching session-creation responses

Most integrations create a small back-end endpoint such as:

POST /api/credit/hosted-session

Your front-end calls this endpoint. This endpoint calls Aarthik Labs.

Next.js App Router Example

Create a route such as app/api/credit/hosted-session/route.ts.

import { NextResponse } from "next/server";
type JourneyType = "PERSONAL_LOAN" | "BUSINESS_LOAN" | "GOLD_LOAN";
type HostedSessionRequest = {
borrowerProviderID?: string;
journeyType?: JourneyType;
profile?: {
contactNumber?: string;
pan?: string;
panName?: string;
dob?: string;
gender?: "male" | "female" | "transgender";
personalemail?: string;
};
workProfile?: {
employmentType?: "salaried" | "selfEmployed";
officialemail?: string;
income?: string | number;
incomeType?: "monthly" | "annual";
companyName?: string;
udyamNumber?: string;
};
address?: {
addressL1?: string;
addressL2?: string;
city?: string;
state?: string;
pincode?: string;
};
journey?: {
endUse?: string;
creditReportConsent?: boolean;
};
goldLoan?: {
userType?: "individual" | "non-individual";
constitution?: string | null;
jewelleryWeightGrams?: string | number;
purity?: "24K" | "22K" | "21K" | "18K" | "14K" | "9K";
endUse?:
| "marriage"
| "familyFunctions"
| "medicalTreatmentAndEmergencies"
| "travelEducationExpenses"
| "businessExpansion"
| "agricultureAndFarmRelatedNeeds"
| "purchaseOfEquipment"
| "other";
aaID?: string;
requestedAmount?: string | number;
requestedTenureMonths?: number;
};
};
type HostedSessionResponse = {
embedURL: string;
};
export async function POST(request: Request) {
const payload = (await request.json().catch(() => null)) as
| HostedSessionRequest
| null;
const borrowerProviderID = payload?.borrowerProviderID?.trim();
const journeyType = payload?.journeyType;
if (!borrowerProviderID) {
return NextResponse.json(
{ error: "Missing borrowerProviderID." },
{ status: 400 },
);
}
if (journeyType !== "PERSONAL_LOAN" && journeyType !== "GOLD_LOAN") {
return NextResponse.json(
{ error: "journeyType must be PERSONAL_LOAN or GOLD_LOAN." },
{ status: 400 },
);
}
const platformBaseURL = process.env.PLATFORM_BASE_URL;
const platformAPIKey = process.env.PLATFORM_API_KEY;
if (!platformBaseURL || !platformAPIKey) {
return NextResponse.json(
{ error: "Hosted Experience configuration is missing." },
{ status: 500 },
);
}
const response = await fetch(`${platformBaseURL}/api/lab/sessions`, {
method: "POST",
headers: {
Authorization: `Bearer ${platformAPIKey}`,
"Content-Type": "application/json",
},
body: JSON.stringify({
borrowerProviderID,
journeyType,
profile: payload?.profile,
workProfile: payload?.workProfile,
address: payload?.address,
journey: payload?.journey,
goldLoan: payload?.goldLoan,
}),
cache: "no-store",
});
if (!response.ok) {
const details = await response.json().catch(() => ({}));
return NextResponse.json(
{ error: "Failed to create Hosted Experience URL.", details },
{ status: response.status },
);
}
const data = (await response.json()) as HostedSessionResponse;
return NextResponse.json(
{ embedURL: data.embedURL },
{
headers: {
"Cache-Control": "no-store",
},
},
);
}

Product Availability Endpoint

If your app shows product-specific CTAs, call GET /api/lab/features from your back-end and use the returned catalogue to decide what to show.

type HostedLenderProductCatalogue = {
products: Array<{
key: "personalLoan" | "businessLoan" | "goldLoan";
type: "PERSONAL_LOAN" | "BUSINESS_LOAN" | "GOLD_LOAN";
name: string;
available: boolean;
unavailableReason?: string | null;
lenders: Array<{
id: string;
name: string;
available: boolean;
checks: Array<{
key:
| "CREDIT_REPORT"
| "BANK_ACCOUNT_STATEMENT"
| "GST_INFORMATION"
| "UDYAM_INFORMATION"
| "GOLD_INFORMATION";
required: boolean;
}>;
informationNeeded: {
lenderSpecificRequirementsKnown: boolean;
required: Array<{
key: string;
sendIn: string;
}>;
optional: Array<{
key: string;
sendIn: string;
}>;
conditional: Array<{
key: string;
sendIn: string;
}>;
};
}>;
}>;
};
export async function readLenderProductCatalogue() {
const platformBaseURL = process.env.PLATFORM_BASE_URL;
const platformAPIKey = process.env.PLATFORM_API_KEY;
const response = await fetch(`${platformBaseURL}/api/lab/features`, {
method: "GET",
headers: {
Authorization: `Bearer ${platformAPIKey}`,
},
cache: "no-store",
});
if (!response.ok) {
throw new Error("Failed to read lender product catalogue.");
}
return response.json() as Promise<HostedLenderProductCatalogue>;
}

Typical UI checks:

const catalogue = await readLenderProductCatalogue();
const personalLoanAvailable = catalogue.products.some(
(product) => product.key === "personalLoan" && product.available,
);
const lenderFields =
catalogue.products
.find((product) => product.key === "personalLoan")
?.lenders.at(0)?.informationNeeded.required ?? [];

Error Handling

Handle platform responses as follows:

StatusMeaningRecommended action
400Request shape or required data is invalid.Fix the payload before retrying.
401API key is missing, invalid, or from the wrong environment.Check server-side configuration and key rotation.
404Tenant, application, borrower, or requested journey scope could not be resolved.Verify API key scope and product availability.
500Unexpected platform error.Retry later and share request context with Aarthik Labs support if it persists.

Important Rules

  • Send profile.contactNumber for all journeys. It is a required field.
  • Send journeyType explicitly so product selection is predictable.
  • Create a fresh embedURL when the borrower returns later.
  • Do not persist or expose platform API keys in front-end code, mobile apps, logs, or analytics events.