Subscription & distribution
Catalogs published to the Catalog Service (CATALG) do not automatically reach Discovery Services — a Discovery Service (DS) must first subscribe to CATALG.
Catalogs published to the Catalog Service (CATALG) do not automatically reach Discovery Services — a Discovery Service (DS) must first subscribe to CATALG. Once subscribed, every catalog published or updated after that point that matches the DS's declared filters is pushed to it automatically, in real time. Catalogs that already existed before subscribing are not replayed on subscribe — retrieve those once via POST /catalog/pull. This guide explains how to set up a subscription, receive catalog pushes, and retrieve existing catalogs in bulk.
For the overview and end-to-end sequence, see Publish Catalogs using CATALG. For catalog structure, types, directives, and publishing rules, see Catalog Concepts. For exact endpoint shapes and field-level reference, see API Reference.
How a Discovery Service Subscribes to Catalogs
A Discovery Service goes through two phases: a one-time setup, and then ongoing catalog delivery.
Day 1 — Setup
DS DeDi Registry CATALG │ │ │ │── register + publish ────────►│ │ │ subscriber record │ │ │◄─ subscriber_id + record_id ──│ │ │ │ │ │── POST /catalog/subscription ────────────────────────────►│ │ (signed with private key) │ │ │◄─ subscriptionId, ACTIVE ─────────────────────────────────│ │ │ │ │── POST /catalog/pull ─────────────────────────────────────►│ ← bulk sync │◄─ 200 ACK ────────────────────────────────────────────────│ │ │ │ │◄─ POST /catalog/on_pull ──────────────────────────────────│ ← async delivery │ (existing catalogs) │ │
Ongoing — Real-Time Push
Provider Node CATALG DS │ │ │ │── POST /catalog/publish►│ │ │ │ │ │ │ [match subscriptions] │ │ │ │ │ │── POST /catalog/push ────►│ │ │ │── index catalog │◄─ on_publish ──────────│ │ make searchable
Before You Subscribe
Before calling any CATALG subscription API, the Discovery Service must have a registered entry in the DeDi registry. This entry establishes three things that CATALG depends on at runtime:
DeDi field | What CATALG uses it for |
|---|---|
subscriber_id | Identifies the DS — CATALG binds the subscription to this identity |
signing_public_key | Verifies that every API request genuinely came from the DS |
url | Where CATALG delivers catalog/push and catalog/on_pull — resolved from DeDi at delivery time, never taken from the request body |
Example DeDi subscriber record for a Discovery Service:
{ "subscriber_id": "ds.example.com", "url": "https://ds.example.com/beckn/", "type": "DS", "domain": "*", "countries": ["IND"], "signing_public_key": "<base64-ed25519-public-key>", "encr_public_key": "<base64-encryption-public-key>" }
Once the record is published, DeDi assigns a record_id. Together with subscriber_id, this forms the keyId used in every request:
keyId="ds.example.com|<record_id>|ed25519"
For the full DeDi registration walkthrough — namespace creation, domain verification, publishing a subscriber record — see Onboarding Network Participants.
Two Ways to Subscribe
Option 1 — Direct API Integration
The Discovery Service calls CATALG directly using the fabric endpoint, signing each request with its own Ed25519 private key.
DS signs request with own private key → POST https://fabric.nfh.global/beckn/catalog/subscription → CATALG verifies the signature using the DS's public key from DeDi → subscription created, bound to ds.example.com identity → on each matching publish, CATALG resolves the callback URL from DeDi → POST catalog/push to https://ds.example.com/beckn/catalog/push
Use this option when the Discovery Service is a standalone system managing its own Fabric identity and signing.
Option 2 — Using ONIX
The Discovery Service is deployed alongside ONIX. ONIX holds the DS's identity and private key from its configuration, and handles all signing and protocol mechanics automatically. The DS simply tells ONIX what to subscribe to.
DS tells ONIX: subscribe to these networkIds / schemaTypes → ONIX signs + POSTs /catalog/subscription using its configured identity → catalog/push arrives at ONIX receiver endpoint → ONIX forwards the catalog to the DS
Use this option when the Discovery Service is part of a full ONIX deployment and does not need to manage signing directly.
Signing Requests
Every request to CATALG must include a Beckn HTTP Signature in the Authorization header, built from the DS's Ed25519 private key and a hash of the request body. If you are using Option 2 (ONIX), ONIX handles all of this automatically. If you are integrating directly (Option 1), see the API Reference for the full signing specification.
Real-Time Push (Subscription-Driven)
Understanding networkIds
A networkId identifies a Fabric network — a curated set of participants governed by a Network Facilitator Organization. The recommended format is namespace_id/registry_id, for example local-retail.net/grocery-net.
When a Discovery Service subscribes to a networkId, it receives every catalog published by any Provider Node that belongs to that network. The networkId to subscribe to is provided by the network operator. A single subscription can cover multiple networks by listing more than one networkId.
Subscribing
A Discovery Service subscribes to CATALG once using POST /catalog/subscription, declaring which networkIds and/or schemaTypes it wants to receive. From that point on, every catalog publish accepted by CATALG is automatically evaluated against all active subscriptions. Every DS whose filters match receives its own POST /catalog/push delivery — independently and in parallel. This is the fan-out:
Provider Node ──POST /catalog/publish──► CATALG │ ┌───────────────────┼───────────────────┐ ▼ ▼ ▼ DS (sub A) DS (sub B) DS (sub C) POST /catalog/push POST /catalog/push POST /catalog/push (networkId match) (networkId match) (schemaType match)
Each Discovery Service receives only the catalogs that match its own subscription filters. Sub A and Sub B may be subscribed to the same networkId; Sub C may be subscribed by schemaType alone — CATALG evaluates each independently. A single publish can fan out to any number of Discovery Services simultaneously.
Subscription filters narrow which catalogs a DS receives. At least one of networkIds or schemaTypes must be provided. An empty schemaTypes array matches all schema types for the subscribed networks.
POST /catalog/subscription Authorization: Signature keyId="ds.example.com|<record_id>|ed25519",algorithm="ed25519",created="...",expires="...",headers="(created) (expires) digest",signature="..."
{ "context": { "version": "2.0.0", "action": "catalog/subscription", "messageId": "a1b2c3d4-0000-0000-0000-000000000001", "transactionId": "a1b2c3d4-0000-0000-0000-000000000002", "timestamp": "2026-05-27T08:00:00.000Z" }, "message": { "subscription": { "networkIds": ["local-retail.net/grocery-net"], "schemaTypes": ["https://schema.beckn.io/RetailResource/2.1/context.jsonld"] } } }
CATALG responds synchronously with a subscriptionId and status: ACTIVE:
{ "context": { "version": "2.0.0", "action": "catalog/on_subscription", "messageId": "a1b2c3d4-0000-0000-0000-000000000001", "transactionId": "a1b2c3d4-0000-0000-0000-000000000002", "timestamp": "2026-05-27T08:00:00.100Z" }, "message": { "subscriptions": [ { "subscriptionId": "f47ac10b-58cc-4372-a567-0e02b2c3d479", "status": "ACTIVE", "networkIds": ["local-retail.net/grocery-net"], "schemaTypes": ["https://schema.beckn.io/RetailResource/2.1/context.jsonld"], "createdAt": "2026-05-27T08:00:00.000Z", "updatedAt": "2026-05-27T08:00:00.000Z" } ] } }
Save the subscriptionId — you will need it to pull existing catalogs and to manage your subscription later.
Callback URL is never in the subscription request. CATALG resolves the DS's callback endpoint from its DeDi registry entry at delivery time. The url field in the DeDi record is the base — CATALG appends the action path to form the full delivery URL (e.g. https://ds.example.com/beckn/catalog/push).
Managing Your Subscription
To update your subscription filters or deactivate a subscription, use the subscription management endpoints. See the API Reference for details on updating and deactivating subscriptions.
Receiving catalog/push
After subscribing, CATALG will POST to the Discovery Service's registered endpoint every time a matching catalog is published. The DS must implement POST /catalog/push to receive these deliveries.
Subscribing does not deliver existing catalogs. A successful subscription only returns a subscriptionId with status: ACTIVE — no catalog/push follows. Only catalogs published or updated after the subscription becomes active are pushed. To load catalogs that already exist in CATALG, call POST /catalog/pull once after subscribing (see Catalog Pull). The typical DS bootstrap is therefore: subscribe → pull once → then live on push.
The push payload carries the catalog in the same format as a published catalog, with context.action set to catalog/push:
{ "context": { "version": "2.0.0", "action": "catalog/push", "messageId": "b2c3d4e5-0000-0000-0000-000000000010", "transactionId": "b2c3d4e5-0000-0000-0000-000000000011", "networkId": "local-retail.net/grocery-net", "timestamp": "2026-05-27T09:15:00.000Z" }, "message": { "catalogs": [ { "id": "CAT-GROCERY-001", "descriptor": { "name": "Fresh Produce Catalog" }, "provider": { "id": "PROV-001", "descriptor": { "name": "Green Farms" } }, "resources": [ ... ] } ] } }
The DS must respond with 200 Ack to confirm receipt:
{ "message": { "status": "ACK", "messageId": "b2c3d4e5-0000-0000-0000-000000000010" } }
Accept first, process async. Acknowledge immediately and index the catalog in the background. A slow or blocking response risks a delivery timeout. Return 200 Ack as soon as the payload is received and validated.
Verify the signature. CATALG signs every push request with its own Beckn HTTP Signature. The DS should verify this against CATALG's public key in DeDi before processing the payload.
If your endpoint is unavailable. CATALG will retry failed deliveries. If your endpoint is persistently unreachable, use POST /catalog/pull to re-sync any missed catalogs once your service is back online.
Catalog Pull (Bulk Retrieval)
POST /catalog/pull is how a Discovery Service requests all existing catalogs from CATALG in one operation. It is the mechanism for initial data population and for re-synchronising after an outage.
The pull is fully asynchronous:
DS sends POST /catalog/pull → receives immediate ACK
CATALG assembles the matching catalogs in the background
CATALG resolves the DS's callback URL from DeDi and delivers results via POST /catalog/on_pull
DS processes the received catalogs and makes them available for discovery
The pull request is scoped to an existing active subscription — the subscriptionId is required, and CATALG uses that subscription's networkIds and schemaTypes to determine which catalogs to return. A DS must have an active subscription before issuing a pull.
Ownership enforced: A DS can only pull using a subscriptionId it created. Attempting to use another DS's subscriptionId will be rejected, as will a subscriptionId that does not exist.
POST /catalog/pull Authorization: Signature keyId="ds.example.com|<record_id>|ed25519",algorithm="ed25519",created="...",expires="...",headers="(created) (expires) digest",signature="..."
{ "context": { "version": "2.0.0", "action": "catalog/pull", "messageId": "e1f2a3b4-0000-0000-0000-000000000099", "transactionId": "e1f2a3b4-0000-0000-0000-000000000100", "timestamp": "2026-05-27T10:00:00.000Z" }, "message": { "subscriptionId": "f47ac10b-58cc-4372-a567-0e02b2c3d479", "mode": "FULL", "limit": 20, "offset": 0 } }
CATALG responds immediately with an ACK and then delivers results asynchronously to the DS's registered callback URL.
{ "message": { "status": "ACK", "messageId": "e1f2a3b4-0000-0000-0000-000000000099" } }
Callback — Inline Payload
When the result set is small, CATALG delivers the catalogs inline in the catalogs array:
{ "context": { "version": "2.0.0", "action": "catalog/on_pull", "messageId": "e1f2a3b4-0000-0000-0000-000000000099", "transactionId": "e1f2a3b4-0000-0000-0000-000000000100", "timestamp": "2026-05-27T10:01:05.000Z" }, "message": { "status": "COMPLETED", "catalogs": [ ... ], "pagination": { "total": 12, "limit": 20, "offset": 0 } } }
Callback — Large Payload (Download Manifest)
When the result is too large to return inline, CATALG returns a downloadManifest instead. The DS must download the payload from the provided URL, verify the checksum, and then process the content.
{ "context": { "version": "2.0.0", "action": "catalog/on_pull", "messageId": "e1f2a3b4-0000-0000-0000-000000000099", "transactionId": "e1f2a3b4-0000-0000-0000-000000000100", "timestamp": "2026-05-27T10:01:05.000Z" }, "message": { "status": "COMPLETED", "downloadManifest": { "url": "https://storage.example.com/catalog-pull-results/e1f2a3b4.json.gz?token=...", "expiresAt": "2026-05-27T11:01:05.000Z", "format": "json.gz", "sizeBytes": 4823104, "checksum": "sha256:a3f1e9b7c2d04..." }, "pagination": { "total": 1450, "limit": 20, "offset": 0 } } }
Field | Description |
|---|---|
url | Pre-signed download URL. Do not attempt download after expiresAt |
expiresAt | Expiry timestamp — if expired, re-issue POST /catalog/pull |
format | json or json.gz — decompress before parsing if json.gz |
sizeBytes | Payload size in bytes; use this to verify the download is complete |
checksum | sha256: prefixed hex digest — verify before processing; discard if verification fails |
catalogs[] and downloadManifest are mutually exclusive. Exactly one must be present when status is COMPLETED. When status is FAILED, neither is present — read the error field instead.