Deep technical documentation of the Unified Bharat e-Charge protocol — architecture, flows, APIs, and network benefits.
The Unified Bharat e-Charge (UBC) protocol is built on the Beckn Protocol, a decentralized, open standard that enables peer-to-peer commerce between consumer applications and service providers. For EV charging, this means any EV driver app (BAP) can discover and book charging services from any charge point operator (BPP) on the network — without proprietary lock-in.
The protocol maps EV charging semantics onto Beckn's commerce primitives:
| EV Domain Entity | Beckn Concept | Description |
|---|---|---|
| Charging Connector | Item | Each physical connector on a charging station is one Item with a unique ID following the connector nomenclature. |
| Charging Tariff / Service | Offer | Pricing plans attached to items — per-kWh, per-minute, or flat-rate with idle fee policies. |
| Charging Session / Booking | Order | The lifecycle of a charging session from creation through completion, including payment and fulfillment. |
The network operates on protocol version 2.0.0 with a domain identifier beckn.one:deg:ev-charging and a default message TTL of PT30S (30 seconds).
Every UBC-enabled charging connector has a QR code placed on or near the socket. Scanning this code with any UBC-compatible app triggers discovery for that specific connector — no app-specific scanning required.
beckn://discover?&message.catalogs.items.provider.id={PROVIDER_ID}&message.catalogs.items.id={ITEMS_ID}&version=1.0
<country-code>*<operator-id>*<station-id>*<point-id>*<connector-id>
Example: IND*ECO*BTM*01*CCS2-A
IND)* (asterisk)ECO for EcoPower)BTM for BTM layout)01)CCS2-A)
discover BAP → CDSThe user opens their EV app and either scans a QR code on a charger or searches for nearby chargers. The BAP sends a discover request to the CDS.
"message": {
"filters": {
"type": "jsonpath",
"expression": "$[?(@.beckn:itemAttributes.qrIdentifier == 'https://charge.ecopower.in/cs01-ccs2')]"
}
}
Filters by vehicleType, connectorType, availabilityWindow time range, and spatial geo filter (s_dwithin with Point + distanceMeters).
on_discover CDS → BAPThe CDS responds with a catalog of available connectors. If cached data exists, it responds immediately. Otherwise, it fans out to non-caching BPPs who respond individually.
connectorType (CCS2, Type2, CHAdeMO), maxPowerKW/minPowerKW, GPS coordinates, amenityFeature[] (RESTROOM, RESTAURANT, WI-FI), evseId, chargingSpeed (FAST/ULTRAFAST/NORMAL), vehicleType, rating (ratingValue + ratingCount), availabilityWindowtariffModel (PER_KWH), price (₹45/kWh), idleFeePolicy (₹2 per 10 min idle), acceptedPaymentMethod[] (UPI, CREDIT_CARD, WALLET), validity date range
select BAP → BPPThe user selects a charger. The BAP sends select to the BPP that owns that connector.
orderStatus: CREATEDbuyer — id, role, displayName, telephone, email, taxID/GSTINorderItems[] — connector ID + estimated quantity (e.g., 2.5 kWh) + accepted offerorderAttributes.buyerFinderFee — {feeType: PERCENTAGE, feeValue: 2.5}preferences.startTime / endTimeon_select BPP → BAPThe BPP returns a pricing quote. orderValue contains exactly 4 component types:
If the slot is already booked: orderStatus: REJECTED, error code 40000.
"orderValue": {
"value": "143.95", "currency": "INR",
"components": [
{"type": "UNIT", "value": "112.50"},
{"type": "SURCHARGE", "value": "40.00"},
{"type": "DISCOUNT", "value": "-15.00"},
{"type": "FEE", "value": "6.45"}
]
}
init BAP → BPPThe user confirms the quote and enters payment details. The BAP sends init with billing information and the buyer's UPI ID.
buyer.buyerAttributes — BuyerUPI extension with vpa field (e.g., ravikumar@upi) — used for refundspayment object:
amount — pre-authorized amount (e.g., ₹143.95)beneficiary — BPP or BAP (who collects payment — negotiable)paymentStatus: INITIATEDpaymentAttributes — UBCPaymentAttributes with upiTransactionId and settlementAccounts[]BAP-Led Payment: If beneficiary: BAP is set, the BAP collects payment directly. The BPP's on_init response is authoritative — if the BPP echoes BAP, BAP collects; if BPP, the BPP retains collection rights.
on_init BPP → BAPThe BPP assigns an order.id and returns payment details:
payment.id, paymentURL, txnRefacceptedPaymentMethod[] — UPI, BANK_TRANSFER, WALLETsettlementAccounts[] — both BAP and BPP beneficiary accounts (accountHolderName, accountNumber, ifscCode, vpa)UPI-Native Flow: The BPP retrieves a dynamic UPI intent link from the CPO's payment gateway. This is redirection-less — the payment gateway verifies credit in real-time with the UPI network. No browser redirects, no separate payment app switching.
on_status (unsolicited) BPP → BAPOnce the payment gateway confirms the UPI transaction, the BPP sends an unsolicited on_status to the BAP:
paymentStatus: COMPLETEDpaidAt — timestamp of payment confirmationpaymentAttributes.upiTransactionId — UPI reference numberorderStatus → PENDING — order is now ready for confirmationconfirm BAP → BPPThe BAP confirms the order with orderStatus: PENDING and the COMPLETED payment reference. The BPP locks the connector and prepares for the charging session.
on_confirm BPP → BAPThe BPP confirms the booking with orderStatus: CONFIRMED and returns fulfillment details:
fulfillment.idfulfillment.mode: RESERVATIONdeliveryAttributes (@type ChargingSession):
connectorType — CCS2, Type2, etc.maxPowerKW — maximum power outputsessionStatus: PENDING — waiting for user to plug instatus / on_status — Connector CheckBefore starting, the BAP can check if the connector gun has been physically connected:
status (order in CONFIRMED state)deliveryAttributes.connectorStatus: PREPARING (OCPP status — gun is connected, ready to charge)
update BAP → BPPThe user plugs in the connector and taps "Start Charging" in the app. The BAP sends update to begin the session.
on_update BPP → BAPThe BPP starts the physical charging session:
orderStatus: INPROGRESSsessionStatus: ACTIVEtrack / on_track — Live TelemetryDuring charging, the BAP can request real-time state-of-charge:
track (order INPROGRESS)fulfillment.trackingAction.target.url (session tracking URL) and deliveryAttributes.chargingTelemetry[]| Metric | Unit Code | Description |
|---|---|---|
| STATE_OF_CHARGE | % | Battery percentage |
| POWER | KWH | Current power draw |
| ENERGY | KW | Cumulative energy delivered |
| VOLTAGE | VLT | Current voltage |
| CURRENT | AMP | Current amperage |
on_update (async/unsolicited) BPP → BAPThe charging session stops when: the battery reaches the cost limit, 100% state-of-charge, or the user manually stops. The BPP sends an unsolicited on_update:
orderStatus: COMPLETEDsessionStatus: COMPLETEDinvoiceAttributes.invoiceUrl)If the user stops early, the BAP sends update with fulfillment.deliveryAttributes.sessionStatus: STOP. The BPP responds with on_update containing the finalized Charge Detail Record (CDR) reflecting actual energy consumed.
rating / on_ratingAfter the session, the user rates the charging experience:
ratingValue (1–5 scale, bestRating: 5, worstRating: 1)category: FULFILLMENTfeedback.comments — free-text reviewfeedback.tags[] — categorization tagsThe BPP responds with received: true and a feedbackForm URL + submission_id for extended feedback.
support / on_supportIf the user needs help, a structured support flow opens:
refId (order id), refType: ORDERfeedback — SupportFeedback extension with comments, tags[] (e.g., ["charging-interrupted", "billing-dispute"])supportStatus lifecycle: OPEN → ACKNOWLEDGED → IN_PROGRESS → RESOLVED → CLOSED (or ESCALATED)cancel / on_cancelCancellation is only possible while sessionStatus is PENDING (before charging starts). Once ACTIVE, use update with sessionStatus: STOP instead.
orderStatus: CANCELLEDpayment.beneficiary: BUYERpaymentStatus: REFUNDED — refund processed to buyer's VPABoth user-initiated (cancel) and provider-initiated (unsolicited on_cancel) are supported.
Since the pre-authorized amount is an estimate, the actual energy consumed may differ. UBC handles two settlement scenarios:
Scenario: Pre-authorized ₹143.95 (2.5 kWh @ ₹45/kWh), but actual consumption was only 2.0 kWh (₹90.00). Excess ₹53.95 must be refunded.
on_update (Completion): orderValue.value: 90.00 (actual), with a negative DISCOUNT component of −₹53.95 labeled "Refund adjustment — excess amount to be refunded". paymentStatus remains COMPLETED.on_status (Refund Confirmed): Once the payment gateway confirms the refund (SLA ~7 business days), the BPP sends on_status with paymentStatus: REFUNDED and payment.amount.value: 53.95 (the refund amount).Detection: Compare payment.amount.value (paid) vs orderValue.value (actual). If paid > actual → undercharge. Look for negative DISCOUNT component.
Scenario: Pre-authorized ₹143.95 but actual consumption was 5.0 kWh (₹287.90). The uncollected ₹143.95 is recorded against the user.
on_update (Completion): orderValue.value: 287.90 (full actual). The FEE component includes ₹112.50 for excess units consumed. payment.amount.value: 143.95 (only pre-auth collected), paymentStatus: COMPLETED.on_select: When the same user starts a new session with the same CPO, the orderValue includes an additional FEE component of ₹143.95 with description "Outstanding balance carried forward from previous overcharge session". The BAP should surface this to the user.Detection: If paid < actual → overcharge. In the next on_select, look for a FEE component with "Outstanding balance" in the description.
Every connector on the network carries rateable: true and a rating object with ratingValue and ratingCount. This data is visible during discovery — users can compare chargers by quality (e.g., 4.7★ from 89 ratings vs 4.3★ from 156 ratings) before selecting one. Post-session ratings are captured via structured feedback (1–5 scale + comments + tags on the FULFILLMENT category), creating a transparent, self-improving quality signal across all CPOs.
The CDS (Catalog Discovery Service) caches BPP catalogs, decoupling discovery from real-time BPP availability. If a BPP goes temporarily offline, the CDS can still service discovery requests from cache — the user sees available chargers without interruption. If cached data has no availability, the CDS falls back to fan-out, querying non-caching BPPs directly. Charging interruptions trigger immediate unsolicited on_status (sessionStatus: INTERRUPTED) followed by on_update reconciliation, ensuring both BAP and BPP converge on true state without manual intervention.
Because UBC is built on an open protocol (not a closed platform), any CPO can join by implementing a Beckn endpoint and getting certified by NBSL. Any eMSP can build a BAP. This means more charging networks visible to users, more consumer apps to choose from, and healthy competition driving down prices and improving service quality. The connector nomenclature (IND*operator*station*point*connector) ensures every charger is uniquely identifiable across the entire national network.
The network collects structured data about every session — success rates, failure modes, interruption frequency, charger uptime, and user ratings. This data is visible to the NO (NBSL) and informs certification decisions. Over time, the network builds a comprehensive reliability picture: which CPOs have the best uptime, which connector types are most reliable, which locations have the highest success rates. This data-driven feedback loop incentivizes CPOs to maintain high standards, as poor performance directly impacts their visibility in discovery results.
on_select validates against existing PENDING/ACTIVE bookings and rejects conflicts with error code 40000.init stage when a connector is marked reserved, preventing double-booking.Technical specification based on UBC TSD v0.9 from github.com/bhim/ubc-tsd. Protocol version 2.0.0, domain beckn.one:deg:ev-charging.