The complete ONDC transaction lifecycle — SELECT → Form Submit → INIT → Create Order → Confirm. All steps are sequential and each depends on values from the previous.
Critical: The transaction_id returned by SELECT must be passed unchanged to INIT and Create Order. Do not generate a new one. See the Complete Flow Guide for the full picture.
First step of the ONDC transaction. Sends your selected items and fulfillment slot to the provider's BPP. Returns the confirmed price quote, cancellation terms, and optional visitor detail forms.
POST{{base_url}}/select
Bearer Token Required
Request Body — application/json
Field
Type
Required
Source
cityCode
string
REQUIRED
GET /item/slug: provider.cityCode
bppId
string
REQUIRED
GET /item/slug: provider.bppId
bppUri
string
REQUIRED
GET /item/slug: provider.bppUri
providerId
string
REQUIRED
GET /item/slug: provider.id
items
array
REQUIRED
See structure below
fulfillments
array
REQUIRED
See structure below
items[n] Structure
{
"id": "019be49d-..._9000.00_1000", // GET /tickets: data[n].id (composite — use as-is)"parent_item_id": "019be49c-...", // GET /tickets: data[n].parentItemId"quantity": {
"selected": { "count": 2 } // User-selected quantity
},
"fulfillment_ids": ["019be49e-48f1-71c0-..."] // GET /timeslots: data[n].id
}
fulfillments[n] Structure
{
"id": "019be49e-48f1-71c0-89c6-a0aae9aa90b9", // GET /timeslots: data[n].id"stops": [{
"type": "START",
"time": {
"range": {
"start": "2026-02-28T03:30:00.000Z", // GET /timeslots: timeRangeStart"end": "2026-02-28T12:30:00.000Z"// GET /timeslots: timeRangeEnd
}
}
}]
}
Response — 201 Created (key fields)
{
"data": {
"onSelect": {
"context": {
"transaction_id": "1ca40299-27bc-4bf9-b6a0-4095190ab1ab"// ← SAVE THIS! Pass to INIT + Create Order unchanged
},
"message": {
"order": {
"quote": {
"breakup": [
{ "title": "BASE_PRICE", "price": { "value": "18000.00" } },
{ "title": "TAX", "price": { "value": "0" } }
],
"price": { "currency": "INR", "value": "18000.00" } // Final amount to display
}
}
}
},
"forms": [...] // If non-empty → call Form Submit before INIT
}
}
Save transaction_id! This ID ties your entire booking session together. It must be passed unchanged to INIT and Create Order. If you lose it, start a new SELECT.
If data.forms is non-empty, you must call Form Submit before proceeding to INIT.
Submit additional visitor details required by certain providers (e.g., ASI monuments need visitor name, age, nationality). Only call this if SELECT returns a non-empty forms[] array.
POST{{base_url}}/form/group/:groupId
No Auth Required
Path Parameters
Field
Type
Required
Source
groupId
string
REQUIRED
SELECT response: data.forms[n].groupId
Request Body — application/json
Field
Type
Required
Description
transactionId
string
REQUIRED
From SELECT: onSelect.context.transaction_id
data.formId
string
REQUIRED
Hidden field — SELECT: forms[n].fields where name="formId"
data.itemId
string
REQUIRED
Hidden field — SELECT: forms[n].fields where name="itemId"
data.name
string
REQUIRED
Visitor's full name
data.email
string
REQUIRED
Visitor's email address
data.mobile
string
REQUIRED
10-digit mobile number
data.age
string
CONDITIONAL
Required if form has minimum age constraint
data.country
string
OPTIONAL
Default: India
data.gender
string
OPTIONAL
Male / Female / Other
data.countrycode
string
OPTIONAL
Phone country code. Example: +91
Multiple Submissions: Check forms[n].totalSubmissionsRequired. If the user booked 2 tickets, submit the form twice with different visitor details. Proceed to INIT only when the response forms[] array is empty.
Hidden fields: Fields with "type": "hidden" must be included in the data payload but should NOT be shown in the UI. Include formId and itemId exactly as returned.
Initializes the order with billing details. Returns the final payment amount, a payment ID, and BPP settlement terms. Must be called after SELECT (and Form Submit if required).
POST{{base_url}}/init
Bearer Token Required
Request Body — application/json
Field
Type
Required
Source
cityCode
string
REQUIRED
Same cityCode used in SELECT
transactionId
string
REQUIRED
SELECT → onSelect.context.transaction_id — must be identical to SELECT
bppId
string
REQUIRED
Same bppId from SELECT
bppUri
string
REQUIRED
Same bppUri from SELECT
providerId
string
REQUIRED
Same providerId from SELECT
items
array
REQUIRED
Same items[] array from SELECT — unchanged
fulfillments
array
REQUIRED
Same fulfillments[] array from SELECT — unchanged
userName
string
REQUIRED
Billing user's full name
userEmail
string
REQUIRED
Billing user's email address
userMobile
string
REQUIRED
10-digit mobile number (no country code)
attractionId
string
OPTIONAL
Item UUID from GET /item/slug: data.id — for analytics tracking
Creates a payment order in the platform. Returns the Razorpay order ID (rpOrderId) needed to launch the payment gateway on the frontend.
POST{{base_url}}/orders
Bearer Token Required
Request Body — application/json
Field
Type
Required
Description
device
string
REQUIRED
Platform: WEB, ANDROID, IOS
transactionId
string
REQUIRED
Same transaction_id from SELECT/INIT (unchanged)
pg
string
REQUIRED
Payment gateway: RAZORPAY. Check GET /pg-config for available options.
userId
string
OPTIONAL
Logged-in user's UUID from JWT token
Response — 201 Created
{
"data": {
"orderId": "HDBAP0000000166", // ← SAVE: poll GET /orders/:orderId for booking status"pg": "RAZORPAY",
"rpOrderId": "order_SUbGfZnuak5rGb", // ← Pass to Razorpay SDK to open checkout modal"pgOrderId": "order_SUbGfZnuak5rGb",
"orderStatus": "INITIATED"// Expected at this stage
}
}
Opening Razorpay Checkout
// Example Razorpay SDK usage
var rzp = new Razorpay({
key: "<RAZORPAY_KEY_ID>",
order_id: "order_SUbGfZnuak5rGb", // rpOrderId from response
amount: 900000, // amount in paise (9000 INR × 100)
currency: "INR",
name: "Entry Pass",
handler: function(response) {
// Payment success — now poll GET /orders/:orderId
}
});
rzp.open();
Do NOT call CONFIRM manually. After successful payment, the Razorpay webhook automatically triggers the ONDC CONFIRM flow. Your job is simply to poll GET /orders/:orderId to check the result.
Poll the booking status after payment completes. Since ONDC CONFIRM is asynchronous (BPP may take time to respond), keep polling until both orderStatus = SUCCESS and bookingStatus = COMPLETED.
GET{{base_url}}/orders/:orderId
Bearer Token Required
Path Parameters
Field
Type
Required
Description
orderId
string
REQUIRED
HD platform order ID from Create Order response. Example: HDBAP0000000166