> For the complete documentation index, see [llms.txt](https://docs.nfh.global/llms.txt). Markdown versions of documentation pages are available by appending `.md` to page URLs; this page is available as [Markdown](https://docs.nfh.global/build/migration-guides/beckn-v1-to-v2.md).

# Migrating from Beckn protocol version 1 to version 2

A readiness checklist for moving a Beckn network participant from protocol v1 to v2 — covering DEDI registry setup, protocol and schema readiness, ONIX configuration, and business application migratio

## Executive Summary

Moving a Beckn network from v1 to v2 is not just a payload rename. V2 introduces improved identity and discovery, a JSON-LD-based protocol contract, and changes to adapter configuration — and it also touches how the business application connects to the network. This guide is a shared readiness checklist for that move, written for network participants, network facilitator organizations, and business application teams who need to align before implementation starts.

It walks through what a participant must get right, in the order it typically comes up: registering identity on the DEDI registry, meeting the v2 protocol and schema requirements, configuring ONIX correctly, and planning the business application's migration.

## 1. DEDI registry and participant records

The v2 migration path does not depend on a separate gateway. Instead, trust and participant discovery are established through the DEDI registry and its participant records.

### DEDI registry readiness

Register on the [DEDI registry](https://publish.dedi.global/) before starting any other DEDI-dependent step below. Registration involves four steps:

1. **Namespace creation** — Create and verify the participant's namespace by proving domain ownership. This establishes the root of trust for everything published under it.
2. **Registry publication** — Add a registry under that namespace, following the NFH fabric subscriber schema, to list Beckn subscriber details.
3. **Subscriber record creation** — Publish subscriber records for each participant, including callback URL, network role (consumer/provider node, etc.), domain code, and public key.
4. **ONIX interaction** — Confirm that ONIX automatically resolves subscriber details (keys, roles, callback URLs) during network interactions. No additional registry API integration is required on the application side.

**Reference**

* If your organization is a **Network Participant (NP)** registering subscriber details on an existing network, follow [Network Participants registering on the NFH fabric](/build/creating-a-network/onboarding-participants.md#network-participants-registering-on-the-fabric).
* If your organization is instead a **Network Facilitator Organization (NFO)** setting up a new network, follow [Setting up the network environment](/build/creating-a-network/environment.md).
* For background on the service itself, see the [REGISTR product overview](/product-documentation/products/registr.md).

## 2. Protocol and schema readiness

For a new v1-to-v2 migration, start by creating the domain-specific schema, if it does not exist already, before anything else in this section — ONIX's validator has nothing to validate against until that schema exists and is hosted. Once it's in place, the rest of this section — the core spec, hosting, and how the validator resolves it — follows.

Primary source: Beckn v2 protocol specification, `beckn.yaml`

Specification URL: [beckn.yaml](https://raw.githubusercontent.com/beckn/protocol-specifications-v2/refs/heads/main/api/v2.0.0/beckn.yaml)

Use `beckn.yaml` as the source of truth for v2 actions, schemas, response formats, signing expectations, and validation behavior. Confirm the exact protocol version and supported actions before making any code or ONIX configuration changes.

Schema validation itself is automatic: ONIX's `schema2validator` plugin validates every payload against both the core protocol schema and any domain-specific extended schema. Teams don't need to write custom validation code — the readiness work is making sure both schemas are correctly in place.

### Core protocol vs. domain-specific schema

* **Domain-specific extended schema** — Create this first, before any other schema readiness step. It covers domain-specific fields (for example, agri-procurement or soil-testing attributes) that aren't covered by the core schema. A separate extended schema is required for each domain migrating to v2.
* **Core protocol** — `beckn.yaml`, referenced above. Covers actions, context, and structure common to every domain.
* `schema2validator` doesn't need the extended schema URL configured manually — it fetches the schema automatically from the `@context` field carried in the incoming payload's JSON-LD context.
* Host the extended schema on a raw GitHub URL for testing, and on [schema.nfh.global](https://schema.nfh.global/) for production. Payloads for each environment must reference the matching URL through `@context`.
* Re-host and re-reference the schema whenever the domain schema changes. A stale hosted schema causes `schema2validator` to pass or fail payloads incorrectly.

### What to confirm

* Target protocol version and action list.
* Field-level mapping from v1 to v2 for every supported action.
* Mandatory v2 fields that don't exist in the current v1 payload.
* Business concepts affected by v2 changes, including catalog, offer, contract, performance, fulfillment, status, payment, and settlement data.

## 3. ONIX configuration

ONIX is the adapter layer for the v2 migration path and must be configured for every participant. It handles routing, signing, schema validation, and DEDI registry lookup for v2 network communication.

### Configuration checklist

* Correct ONIX version for Beckn v2.
* Participant role and enabled plugins.
* Application-facing and network-facing endpoints.
* Signing keys, verification behavior, and authorization settings.
* Schema validation for v2 requests and callbacks.
* DEDI registry lookup and participant discovery.
* Restart or redeploy process after configuration changes.

### Adapter config reference

Each role runs its own adapter config file. Confirm both are present and correctly configured before go-live:

| Adapter config    | Role  | Modules configured                                                      |
| ----------------- | ----- | ----------------------------------------------------------------------- |
| `bap-config.yaml` | `bap` | `bapTxnReceiver` (`/bap/receiver/`) and `bapTxnCaller` (`/bap/caller/`) |
| `bpp-config.yaml` | `bpp` | `bppTxnReceiver` (`/bpp/receiver/`) and `bppTxnCaller` (`/bpp/caller/`) |

## 4. Business application migration options

Every network participant (NP) is expected to reach full v2 migration — there is no second, equally valid destination. An NP that cannot get there on the required timeline doesn't get to treat that as a stopping point; it needs a documented, time-boxed exception with a plan to close the gap.

### Primary path — full v2 migration

The application natively produces and consumes Beckn v2 payloads. Every NP should plan for this outcome by default.

**Application responsibilities**

* Update request builders and callback handlers for v2 schemas.
* Update domain models where v2 concepts differ from v1.
* Update catalog, contract, performance, fulfillment, status, payment, and settlement logic.
* Update fixtures, automated tests, and API collections to use v2 payloads.
* Configure ONIX for v2 network connectivity (see [Section 3](#3-onix-configuration)).

### Fallback path — phased migration with reqmapper

Use this path only when an NP does not agree to, or cannot complete, immediate v2 migration — for example, because of a legacy application dependency or a resourcing constraint. While reqmapper is in place, the NP continues to send and receive v1 payloads at the application boundary, and ONIX (configured per Section 3, plus the reqmapper plugin described in the [Reqmapper Configuration Reference](#reqmapper-configuration-reference) below) handles v1-to-v2 and v2-to-v1 transformation.

Critically, using reqmapper is conditional: the NP must be **actively and visibly migrating its application to full v2 support in parallel**. Reqmapper is a bridge for the transition period, not a permanent architecture.

**Application responsibilities**

* Keep existing v1 request and callback behavior stable while the mapper is active.
* Provide complete v1 payload samples for every supported action.
* Confirm every required business field can be represented in the mapped v2 payload.
* Test the full flow through ONIX, not only at the application boundary.
* Run the v2-native migration in parallel, with a tracked plan and milestone dates.
* Set a firm milestone for removing the mapper dependency and reconfiguring ONIX for v2-only connectivity.

### Decision rule

Default to full v2 migration for every NP. Only fall back to reqmapper when an NP cannot migrate on the required timeline, and only with a documented parallel migration plan and a retirement date for the mapper. Reqmapper should never be adopted as a long-term substitute for v2 migration.

## Reqmapper Configuration Reference

This section applies only if you are using the reqmapper fallback path described in [Section 4](#4-business-application-migration-options). If your application is fully v2-native, you can skip it entirely. It covers the mapper's configuration, components, and general transformation behavior only — it is not specific to any single use case or domain.

### Technical overview

The ONIX adapter bridges Beckn protocol v1.x and v2.x networks using a file-based transformation layer called the **request mapper**. Rather than hard-coding payload shapes in the adapter binary, all transformation logic lives in a single YAML file containing **JSONata expressions**. The adapter loads this file at startup and applies the right expression automatically based on the action and the participant role (BAP or BPP).

This section explains how the mapper is wired into ONIX, what it transforms, and how transformations are structured for each Beckn action.

### How the mapper plugs into ONIX

**Configuration**

The mapper is declared as a plugin named `payloadTransformer` in the adapter config (`configs/bpp-config.yaml` / `bap-config.yaml`):

```yaml
payloadTransformer:
  id: reqmapper
  config:
    role: bpp   # or "bap"
    mappingsFile: /app/config/mappings.yaml
```

`role` tells the plugin which side of the mapping to apply. `mappingsFile` is the path to the YAML file mounted into the container.

**The `transformPayload` step**

Each module (Receiver and Caller) defines an ordered list of pipeline steps. The `transformPayload` step is where payload conversion happens:

**BAP Receiver** (`/bap/receiver/`) — receives v2 callbacks from the network, converts to v1 for the BAP app:

`validateSign → addRoute → validateSchema → transformPayload`

**BAP Caller** (`/bap/caller/`) — receives v1 requests from the BAP app, converts to v2 for the network:

`transformPayload → addRoute → sign → validateSchema`

**BPP Receiver** (`/bpp/receiver/`) — receives v2 from the network, converts to v1 for the BPP app:

`validateSign → addRoute → validateSchema → transformPayload`

**BPP Caller** (`/bpp/caller/`) — receives v1 from the BPP app, converts to v2 for the network:

`transformPayload → addRoute → sign → checkPolicy → validateSchema`

The step position determines which payload version the surrounding steps see. On Receiver modules, schema validation runs before `transformPayload` (validating the incoming v2 payload); on Caller modules, it runs after (validating the outbound v2 payload).

### Mapping architecture: bapMappings vs. bppMappings

Each Beckn action in the mappings file has two entries:

```yaml
mappings:
  <action>:
    bapMappings: |
      (JSONata expression...)
    bppMappings: |
      (JSONata expression...)
```

| Entry         | Direction                               | Applied when           |
| ------------- | --------------------------------------- | ---------------------- |
| `bapMappings` | v1 → v2 (outbound) or v2 → v1 (inbound) | adapter running as BAP |
| `bppMappings` | v2 → v1 (inbound) or v1 → v2 (outbound) | adapter running as BPP |

The `role` field in the plugin config (`bap` or `bpp`) determines which entry is used at runtime. This keeps a single mappings file shared between both roles.

### Transformation levels

Transformations happen at two levels in every action:

**1. Context level (universal)**

Every message, regardless of action, carries a context block. The field names changed between v1 and v2:

| v1 field           | v2 field           | Notes                                 |
| ------------------ | ------------------ | ------------------------------------- |
| `domain`           | `networkId`        | Identifies the Beckn network/domain   |
| `bap_id`           | `bapId`            | Consumer platform identifier          |
| `bap_uri`          | `bapUri`           | Consumer platform endpoint            |
| `bpp_id`           | `bppId`            | Provider platform identifier          |
| `bpp_uri`          | `bppUri`           | Provider platform endpoint            |
| `transaction_id`   | `transactionId`    | Correlation ID for a full transaction |
| `message_id`       | `messageId`        | Unique ID per request                 |
| `version: "1.0.0"` | `version: "2.0.0"` | Protocol version literal              |

The mapper always rewrites `version` and all snake\_case → camelCase context fields.

**2. Message/payload level (per-action)**

Each action also transforms the message body. The substantive schema changes between v1 and v2 — such as renamed structures, restructured catalogs, and coded metadata — happen here. Unlike the context-level rewrite above, message-level field mappings are not generic: they depend on the specific v1 payload shape a given business application sends, and the v2 structure the target network expects, for each action (`discover`, `on_discover`, `confirm`, `status`, and so on).

Because those payload shapes differ by use case and domain, exact field-level mapping rules are not enumerated here. Each network's mappings file defines its own `bapMappings` and `bppMappings` JSONata expressions per action, following the entry structure described above. Treat the per-action mapping logic as something to write and review against your specific v1 and v2 payload samples, not something to copy from a generic reference.

### Summary of schema changes (v1 → v2)

The rows below are structural, protocol-level renames that apply regardless of domain. Field-level content within catalogs, orders, and fulfillments varies by use case and is not enumerated here — see the note above.

| Category            | v1                                | v2                                               |
| ------------------- | --------------------------------- | ------------------------------------------------ |
| Context field names | snake\_case (`bap_id`, `bpp_uri`) | camelCase (`bapId`, `bppUri`)                    |
| Protocol version    | `"1.0.0"`                         | `"2.0.0"`                                        |
| Domain identifier   | `context.domain`                  | `context.networkId`                              |
| Catalog items       | `providers[].items[]`             | `catalogs[].resources[]` + `catalogs[].offers[]` |
| Order object        | `message.order`                   | `message.contract`                               |
| Fulfillment status  | `order.fulfillments[].state`      | `contract.performance[].status`                  |
| Geo search          | Intent-based (flat address)       | `filters` (JSONPath) + `spatial` (`S_DWITHIN`)   |

### Adapter configuration

The mapper file path is configured separately in the BAP and BPP adapter configs under `payloadTransformer.config.mappingsFile`. Both roles (BAP and BPP) share the same mappings file — the `role` field in the plugin config (`bap` or `bpp`) determines which entry (`bapMappings` or `bppMappings`) is applied at runtime.

| Adapter config    | Role  | Modules configured                                                                 |
| ----------------- | ----- | ---------------------------------------------------------------------------------- |
| `bap-config.yaml` | `bap` | `bapTxnReceiver` (port 8081, `/bap/receiver/`) and `bapTxnCaller` (`/bap/caller/`) |
| `bpp-config.yaml` | `bpp` | `bppTxnReceiver` (port 8082, `/bpp/receiver/`) and `bppTxnCaller` (`/bpp/caller/`) |

When deploying, the mappings file is mounted into the container and its path is referenced by `mappingsFile`. No adapter code changes are required when updating the mappings — only a container restart is needed.

### Challenges and maintenance considerations

**Mapper must be updated for every new field**

The JSONata expressions in the mappings file explicitly enumerate every field that needs to be carried across. If a new field is added to a v1 payload (by the BPP or BAP application) and is not handled in the corresponding mapping expression, that field will be silently dropped during transformation. The adapter will not throw an error — it simply produces an output that omits the unrecognized field.

This means:

* Any new v1 field added to `order`, `items`, `tags`, `fulfillments`, or `context` must have a corresponding mapping added to `confirm.bapMappings` / `confirm.bppMappings` (and likewise for other actions).
* Any new v2 field added by the network side must be handled in the reverse direction.
* The same applies to new `entitlementIds` codes: if a BPP starts sending a new code in v2, `on_discover.bapMappings` (and the `on_confirm`/`on_status` equivalents) must add a rule to convert it back to the correct v1 `tags` group. Without this, the tag is either dropped or falls through to the generic "unknown" passthrough handler, where one exists.

**No schema-awareness at transform time**

The mapper operates on raw JSON; it has no knowledge of the Beckn schema. If the input payload has an unexpected structure (for example, `items` is an object instead of an array), the JSONata expression may produce an incorrect output silently. Schema validation (the `validateSchema` step) is the safety net, but it runs separately from the transform and only validates the version it receives.

**Bidirectional parity must be maintained**

`bapMappings` and `bppMappings` for a given action are mirror images of each other. When one is updated, the other typically needs a matching update. Letting them diverge can cause round-trip data loss — for example, a field survives the outbound transform but is missing from the inbound response.

### Adding or modifying a mapping

1. Open the mappings file (path configured in the adapter config under `payloadTransformer.config.mappingsFile`).
2. Find the relevant action block (for example, `on_discover:`).
3. Edit the `bapMappings` or `bppMappings` JSONata expression, and update the mirror direction if the change affects round-trip data.
4. Redeploy the adapter — no code changes needed, only a container restart.
5. Verify with the relevant Postman collection covering the affected action.

JSONata expressions have access to the full input payload via `$` and support functions like `$map`, `$filter`, `$merge`, `$append`, `$reduce`, `$string`, `$number`, `$boolean`, `$replace`, `startswith`, and sprintf-style string formatting via the adapter's runtime.


---

# Agent Instructions
This documentation is published with GitBook. GitBook is the documentation platform designed so that both humans and AI agents can read, navigate, and reason over technical content effectively. Learn more at gitbook.com.

## Querying This Documentation
If you need additional information that is not directly available in this page, you can query the documentation by asking a question.

Perform an HTTP GET request on the following URL with the `ask` and `goal` query parameters:

```
GET https://docs.nfh.global/build/migration-guides/beckn-v1-to-v2.md?ask=<question>&goal=<user_goal>
```

`ask` is the immediate question: it should be specific, self-contained, and written in natural language.
`goal` is what the user is ultimately trying to achieve, the reason they need the answer. Sharing it helps GitBook give you a better, more relevant answer. A goal is most helpful when it describes the outcome the user wants rather than restating the question. For example, with `ask=how do I create an API token`, a goal like `automate deployments from our CI pipeline` lets GitBook tailor the answer to that use case.

The response will contain a direct answer to the question and relevant excerpts and sources from the documentation.

Use this mechanism when the answer is not explicitly present in the current page, you need clarification or additional context, or you want to retrieve related documentation sections.
