Where select sits
search is a broadcast — one request, many BPPs answering over several seconds. select is the opposite: a direct, point-to-point call to one BPP, identified by bpp_id and bpp_uri from the catalogue entry the driver tapped.
That shift matters. From select onward every action in the transaction goes to exactly one provider, and the gateway is out of the path. The transaction_id stays constant from the original search all the way through to completion — it is the thread that ties a driver's tap to an eventual charging session.
Building the order
The message carries a partial order: which provider, which item, and how it will be fulfilled. It is deliberately incomplete — no billing details yet, no customer, no payment.
POST https://cpo.electreefi.in/beckn/select
{
"context": {
"domain": "ev-charging:uei",
"action": "select",
"version": "1.1.0",
"bap_id": "emsp.example.com",
"bap_uri": "https://emsp.example.com/beckn",
"bpp_id": "cpo.electreefi.in",
"bpp_uri": "https://cpo.electreefi.in/beckn",
"transaction_id": "txn-4471",
"message_id": "msg-9022",
"timestamp": "2026-08-11T09:00:11Z",
"ttl": "PT30S"
},
"message": {
"order": {
"provider": { "id": "EFI" },
"items": [{
"id": "EVSE-014-02",
"quantity": { "selected": { "measure": { "value": "25", "unit": "kWh" } } }
}],
"fulfillments": [{
"id": "FUL-1",
"stops": [{
"type": "START",
"time": { "timestamp": "2026-08-11T09:12:00Z" }
}]
}]
}
}
}The quantity is where EV charging diverges from most Beckn domains. A driver is not buying a discrete product; they are buying an amount of energy, or an amount of time, or whatever the vehicle will take before they leave. Sending a requested kWh lets the BPP quote a total — but the final figure comes from metering, not from this number.
Reading the quote
The BPP answers 200 ACK immediately, then POSTs on_select to the BAP's bap_uri:
POST https://emsp.example.com/beckn/on_select
{
"context": { "...": "same transaction_id", "action": "on_select",
"message_id": "msg-9022", "bpp_id": "cpo.electreefi.in" },
"message": {
"order": {
"provider": { "id": "EFI" },
"items": [{ "id": "EVSE-014-02" }],
"quote": {
"price": { "currency": "INR", "value": "535.93" },
"breakup": [
{ "title": "Energy (25 kWh @ 18.50)",
"price": { "currency": "INR", "value": "462.50" } },
{ "title": "Service fee",
"price": { "currency": "INR", "value": "10.00" } },
{ "title": "GST (18%)",
"price": { "currency": "INR", "value": "63.43" } }
]
},
"fulfillments": [{
"id": "FUL-1",
"state": { "descriptor": { "code": "AVAILABLE" } },
"stops": [{ "type": "START", "location": { "gps": "28.6270,77.3721" } }]
}]
}
}
}The quote is an estimate
An EV charging quote cannot be binding in the way a retail price is, because nobody knows in advance how much energy the vehicle will accept. Three things move it:
- The vehicle stops early. A battery at 80% tapers and may finish well below the requested kWh.
- The driver leaves early. Unplugging at 12 kWh against a 25 kWh quote halves the energy line.
- Time-based components accrue that the quote could only estimate — idle fees in particular.
Label it as an estimate in the UI. The authoritative figure arrives at the end of the transaction, in the same way an OCPI CDR supersedes a running session cost.
Re-checking availability
Between search and select, seconds to minutes have passed and another driver may have plugged in. select is the correct place to find that out, because nothing has been committed yet and the recovery is cheap.
A BPP that returns a quote for an EVSE that is already occupied has pushed the failure downstream to confirm, where the driver has already entered payment details. Check at select and return an unavailable fulfillment state instead:
"fulfillments": [{
"id": "FUL-1",
"state": { "descriptor": { "code": "UNAVAILABLE" } }
}]On the BAP side, treat an unavailable state as a normal branch rather than an error — offer the next-nearest charger from the results already in hand.
Production lessons
- Keep
transaction_idconstant, rotatemessage_id. One transaction, many messages. Conflating them breaks correlation across the whole chain. - Honour
ttl. A select whose callback arrives after the TTL should be discarded — the driver has moved on and applying a stale quote produces a price they never saw. - Never reserve on select. It is a question, not a commitment. Holding a connector at this stage blocks revenue for a driver who may never confirm.
- Quote from cached tariff data, not a live pricing call. Select volume on an open network is not under your control.
- Return a breakup even when it is one line. Consistency in the response shape is worth more than brevity.
- Validate the signature on the callback. Same rule as every other Beckn message — see signing and the registry.
Frequently asked questions
It narrows a catalogue entry into a draft order and asks one specific BPP what it will cost. The BPP acknowledges immediately and later POSTs an on_select callback containing a quote with a price breakup. Nothing is reserved and no payment is due at this stage.
Not for EV charging. The final amount depends on how much energy the vehicle actually accepts, which nobody knows in advance. The quote should be presented as an estimate, with the authoritative figure arriving at the end of the transaction.
search is a broadcast, usually through the gateway, that many BPPs answer over several seconds. select is a direct point-to-point call to one BPP identified by bpp_id and bpp_uri. From select onward the gateway is out of the path.
At select. Time has passed since search and the EVSE may now be occupied. Returning an UNAVAILABLE fulfillment state at select is far cheaper than failing at confirm, where the driver has already entered payment details.