Discovery without a central marketplace
A driver opens an app and wants every charger near them, regardless of operator. The BAP could hold a list of every BPP on the network and call each one — but that list changes as providers join and leave, and maintaining it in every buyer app recreates the centralisation the network exists to avoid.
The Beckn Gateway (BG) solves it. The BAP sends one search to the gateway; the gateway consults the registry, finds every BPP subscribed to that domain and location, and forwards the search to all of them. Each responds directly to the BAP's bap_uri.
BAP ──search──► GATEWAY ──┬──search──► BPP A ──on_search──┐
├──search──► BPP B ──on_search──┤──► BAP
└──search──► BPP C ──on_search──┘
BAP ──select/init/confirm──► BPP B (gateway not involved)Note the asymmetry: the gateway forwards requests but callbacks go straight back to the BAP. It is a fan-out mechanism, not a proxy, and it never holds the transaction.
Why the gateway leaves after discovery
Once the driver picks a charger, the BAP has that provider's bpp_id and bpp_uri from the catalogue. Every subsequent action — select, init, confirm, status — goes point to point.
This is a deliberate architectural choice with real consequences:
- The gateway is not a bottleneck for transaction volume. It sees discovery traffic only, never orders or payments.
- It is not a single point of failure for live sessions. A gateway outage stops new discovery; it does not interrupt charges already underway.
- It never holds commercial data. Quotes, billing details and orders never pass through it.
- A network can run several gateways without partitioning transactions.
Compare this with an OCPI hub, which stays in the path for every message. Both solve N×N, but Beckn's gateway solves it only for discovery, which is the part that genuinely needs a broadcast.
ACK and NACK
Every Beckn request gets an immediate synchronous response, and it carries no business data at all:
HTTP/1.1 200 OK
{ "message": { "ack": { "status": "ACK" } } }That means “I have accepted this for processing”. The actual answer arrives later as a callback. A NACK means the request was rejected outright and no callback will follow:
HTTP/1.1 200 OK
{
"message": { "ack": { "status": "NACK" } },
"error": {
"type": "CONTEXT-ERROR",
"code": "30001",
"message": "Provider not found for the given bpp_id"
}
}Error types
type | Raised when | Whose problem |
|---|---|---|
CONTEXT-ERROR | The context is malformed — bad domain, missing transaction_id, unsupported version | Sender |
CORE-ERROR | The message violates the core spec | Sender |
DOMAIN-ERROR | Valid Beckn, invalid for this domain — e.g. an unknown EVSE | Sender, or stale data |
POLICY-ERROR | Rejected by network policy | Network governance |
JSON-SCHEMA-ERROR | Payload fails schema validation | Sender |
The practical split: CONTEXT-ERROR and JSON-SCHEMA-ERROR are bugs in your implementation and retrying will not help. DOMAIN-ERROR often means your cached catalogue is stale, and the right response is to re-search rather than retry the same request. POLICY-ERROR needs a human.
Practical gateway behaviour
- Results stream in, they do not arrive together. Fast BPPs answer in a few hundred milliseconds, slow ones take seconds, some never answer. Render progressively.
- Set a UX window of roughly three to five seconds and stop waiting. A driver will not wait for the slowest participant on the network.
- You cannot know how many responses to expect. There is no count. Absence of a response is not an error and must not block the UI.
- Deduplicate across providers. The same physical site can appear from more than one BPP where a CPO is reachable through multiple aggregators.
- As a BPP, answer search from a cached catalogue. Broadcast volume is not under your control, and a live database fan-out per search will not survive network growth.
Production lessons
- Never treat ACK as a business result. The callback is the answer.
- Handle the no-response case explicitly. Silence from a BPP is normal, not exceptional.
- Verify signatures on gateway-forwarded messages twice — the originator's and the gateway's. See signing and the registry.
- Do not route post-discovery actions through the gateway. It is not a proxy and doing so defeats the architecture.
- Distinguish error types in your retry logic. Schema and context errors are bugs; domain errors mean refresh your data.
- Instrument per-BPP response rate and latency. On an open network this is your only visibility into which providers are actually healthy.
Frequently asked questions
It broadcasts discovery. A BAP sends one search to the gateway, which consults the registry and forwards it to every BPP subscribed to that domain and location. Each BPP then responds directly to the BAP's callback address — the gateway forwards requests but does not relay responses.
Because once the buyer has chosen a provider it has that provider's bpp_id and bpp_uri and can talk to it directly. This keeps the gateway out of transaction volume, prevents it from being a single point of failure for live sessions, and means commercial data never passes through it.
ACK is a synchronous receipt meaning the request was accepted for processing; it carries no business data. The real answer arrives later as a separate callback. Showing a user success on ACK is the same mistake as treating an OCPI CommandResponse as a CommandResult.
CONTEXT-ERROR for a malformed context, CORE-ERROR for core spec violations, DOMAIN-ERROR for messages valid in Beckn but invalid for the domain, POLICY-ERROR for network policy rejections, and JSON-SCHEMA-ERROR for schema validation failures. Context and schema errors are implementation bugs; domain errors usually mean stale cached data.
Roughly three to five seconds, rendering results progressively as they arrive. There is no way to know how many BPPs will respond and some never will, so the absence of a response must not block the interface.