RDN Data Ingestion API

Simple, authenticated REST API for retailers to send transaction data to the Adperical Retail Data Network.

Overview

The RDN Ingestion API allows retailers to send point-of-sale transaction data securely to Adperical. This data powers audiences and closed-loop measurement for brand campaigns.

The API is designed to be simple:

Authentication

All requests must include your API key in the X-API-Key header.

HeaderX-API-Key: rdn_your_api_key_here

Your API key is provided when your retailer account is created. It can be found in the Data Config section of your RDN portal.

Security: Treat your API key like a password. Do not share it in client-side code, public repositories, or URLs.

Base URL

URLhttps://adperical.com/api/rdn

Error Handling

All errors return JSON with an error field:

JSON{ "error": "Invalid API key" }
StatusMeaning
200Success
400Bad request — check your payload
401Missing or invalid API key
403Account not active
500Server error — retry or contact support

Ingest Transactions

POST /api/rdn/ingest

Send one or more transactions. Each transaction represents a single purchase event at a store.

Single Transaction

JSON Body{ "store_id": "tawfeer-jnah", "timestamp": "2026-03-30T14:30:00Z", "total": 45.50, "customer_id": "phone:+961XXXXXXXX", "items": [ { "sku": "TM-GARLIC-200", "category": "Sauces", "brand": "Toom Time", "qty": 2, "amount": 8.00 }, { "sku": "HM-CLASSIC-300", "category": "Dips", "brand": "Hummus Time", "qty": 1, "amount": 5.50 } ] }

Batch Ingest

Send multiple transactions in one request using the transactions array:

JSON Body{ "transactions": [ { "store_id": "tawfeer-jnah", "timestamp": "2026-03-30T14:30:00Z", "total": 45.50, "items": [...] }, { "store_id": "tawfeer-rawche", "timestamp": "2026-03-30T14:35:00Z", "total": 22.00, "items": [...] } ] }

Response

JSON Response{ "success": true, "ingested": 2, "retailer": "Tawfeer" }

Transaction Object

FieldTypeRequiredDescription
store_idstringrecommendedUnique identifier for the store location (e.g. "tawfeer-jnah")
timestampISO 8601recommendedTransaction date/time. Defaults to current time if omitted.
totalnumberrecommendedTotal transaction amount
customer_idstringoptionalCustomer identifier (phone, email, loyalty ID). Hashed on ingestion — never stored raw.
itemsarrayrequiredArray of item objects (see below)

Item Object

FieldTypeRequiredDescription
skustringoptionalProduct SKU or barcode
categorystringrecommendedProduct category (e.g. "Dairy", "Sauces", "Beverages")
brandstringrecommendedProduct brand (e.g. "Toom Time", "Taanayel")
qtyintegeroptionalQuantity purchased. Defaults to 1.
amountnumberrecommendedLine item total amount
Privacy: The customer_id field is SHA-256 hashed immediately on ingestion. The raw value is never stored, logged, or accessible. You can send phone numbers, emails, or loyalty IDs — all are hashed identically.

CSV Upload

If API integration isn't feasible, you can upload CSV files directly through the RDN Retailer Portal.

Expected CSV format:

CSVstore_id,timestamp,sku,category,brand,qty,amount,total,customer_id tawfeer-jnah,2026-03-30T14:30:00Z,TM-GARLIC-200,Sauces,Toom Time,2,8.00,45.50,phone:+961XXXXXXXX tawfeer-jnah,2026-03-30T14:30:00Z,HM-CLASSIC-300,Dips,Hummus Time,1,5.50,45.50,phone:+961XXXXXXXX

Each row represents one item. Rows with the same timestamp + customer_id (or same total) are grouped as a single transaction.

Example: cURL

bashcurl -X POST https://adperical.com/api/rdn/ingest \ -H "Content-Type: application/json" \ -H "X-API-Key: rdn_your_api_key_here" \ -d '{ "store_id": "tawfeer-jnah", "timestamp": "2026-03-30T14:30:00Z", "total": 45.50, "items": [ {"category": "Sauces", "brand": "Toom Time", "qty": 2, "amount": 8.00}, {"category": "Dips", "brand": "Hummus Time", "qty": 1, "amount": 5.50} ] }'

Example: JavaScript

javascriptconst response = await fetch('https://adperical.com/api/rdn/ingest', { method: 'POST', headers: { 'Content-Type': 'application/json', 'X-API-Key': 'rdn_your_api_key_here', }, body: JSON.stringify({ store_id: 'tawfeer-jnah', timestamp: new Date().toISOString(), total: 45.50, items: [ { category: 'Sauces', brand: 'Toom Time', qty: 2, amount: 8.00 }, { category: 'Dips', brand: 'Hummus Time', qty: 1, amount: 5.50 }, ], }), }); const result = await response.json(); console.log(result); // { success: true, ingested: 1, retailer: "Tawfeer" }

Example: Python

pythonimport requests response = requests.post( 'https://adperical.com/api/rdn/ingest', headers={ 'Content-Type': 'application/json', 'X-API-Key': 'rdn_your_api_key_here', }, json={ 'store_id': 'tawfeer-jnah', 'timestamp': '2026-03-30T14:30:00Z', 'total': 45.50, 'items': [ {'category': 'Sauces', 'brand': 'Toom Time', 'qty': 2, 'amount': 8.00}, {'category': 'Dips', 'brand': 'Hummus Time', 'qty': 1, 'amount': 5.50}, ], } ) print(response.json()) # {'success': True, 'ingested': 1, 'retailer': 'Tawfeer'}

Partner & Agent Access (MCP / AdCP)

Beyond ingestion, the network exposes its audiences, in-store inventory and measurement to approved buyers — universal buying platforms, agencies, advertisers, and the agents that run on them — through a Model Context Protocol (MCP) server. The server is an AdCP 3.2 Signals agent: get_adcp_capabilities, get_signals and activate_signal follow the published Ad Context Protocol 3.2 schemas, and the network's standing signal catalog is published at /.well-known/adagents.json (data provider domain adperical.com). In-store media, measurement and activation status are network-native tools prefixed rdn_; they make no AdCP Media Buy conformance claim. Access is per partner, under the network's neutrality rules. Status: preview, by approval.

Endpoint and authentication

URLPOST https://adperical.com/api/rdn/mcp Content-Type: application/json Authorization: Bearer rdnp_your_partner_key (or X-Partner-Key: rdnp_your_partner_key)

The server speaks JSON-RPC 2.0 over POST (single requests or batches) and answers with JSON; it does not open a server-sent event stream. Point any MCP client at the URL with the header above, or call it directly. Partner keys are issued by the operator and shown once; only a hash is stored. Every tool is scoped to retailers that have opted in to partner distribution, intersected with the retailers your partner may see, and to segments those retailers have opted in. A retailer that has not opted in is invisible to every partner. Anonymous discovery is not offered.

Tools

ToolAdCPWhat it does
get_adcp_capabilitiesCoreProtocol versions and features: AdCP 3.2, supported_protocols: ["signals"], discovery modes brief and wholesale, idempotency with 24-hour replay.
get_signalsSignalsMatch a natural-language signal_spec (or exact signal_refs) to purchase-based segments. Each listing carries signal_agent_segment_id, signal_ref, signal_type: "owned", the data provider, pricing_options (CPM data fee) and a deployments entry per requested destination. discovery_mode: "wholesale" enumerates the full priced feed with cursor pagination.
activate_signalSignalsRequest deployment to one or more destinations: {type:"platform", platform} for meta, google, the-trade-desk or retailer-media, or {type:"agent", agent_url}. Requires a UUID idempotency_key. The operator approves and exports the hashed audience; once live, the deployment carries the activation_key. action: "deactivate" withdraws it.
rdn_get_activation_statusnativeStatus of an activation and the decisioning-platform segment id once live.
rdn_list_retailersnativeRetailers in your scope and what each offers (audiences, media products).
rdn_get_productsnative, previewBookable in-store surfaces (screens, shelf, end-caps, receipts, loyalty, sampling) with formats, pricing, availability and the measurement attached.
rdn_create_media_buynative, previewBook products for a flight, optionally targeted with signals. Returns a media_buy_id pending operator acceptance.
rdn_get_media_buy_deliverynative, previewStatus and recorded delivery for your media buys.
rdn_get_measurementnativeClosed-loop sales lift: store-holdout difference-in-differences on retailer transaction data, returning incremental revenue, lift and ROAS.

Example: discover and activate a signal

JSON-RPC{ "jsonrpc": "2.0", "id": 1, "method": "tools/call", "params": { "name": "get_signals", "arguments": { "adcp_version": "3.2", "signal_spec": "frequent coffee buyers in Lebanon", "destinations": [{ "type": "platform", "platform": "the-trade-desk", "account": "agency-seat-1" }], "countries": ["LB"], "pagination": { "max_results": 5 } } } }
Result (structuredContent){ "adcp_version": "3.2", "status": "completed", "timestamp": "2026-10-09T08:00:00Z", "message": "1 signal(s) matched \"frequent coffee buyers in Lebanon\".", "signals": [{ "signal_ref": { "scope": "data_provider", "data_provider_domain": "adperical.com", "signal_id": "coffee_lovers" }, "signal_agent_segment_id": "rdn:9f1c…:c4a2…", "name": "Coffee lovers", "description": "Households buying coffee at least twice a month, derived from transaction data.", "value_type": "binary", "signal_type": "owned", "data_provider": "Tawfeer via Adperical Retail Data Network", "pricing_options": [{ "pricing_option_id": "po_cpm_usd", "model": "cpm", "cpm": 1.00, "currency": "USD" }], "deployments": [{ "type": "platform", "platform": "the-trade-desk", "account": "agency-seat-1", "is_live": false, "estimated_activation_duration_minutes": 1440 }], "ext": { "adperical": { "size": 12400, "addressable_reach": 8300, "retailer_id": "9f1c…" } } }], "pagination": { "has_more": false, "total_count": 1 } }
JSON-RPC{ "jsonrpc": "2.0", "id": 2, "method": "tools/call", "params": { "name": "activate_signal", "arguments": { "adcp_version": "3.2", "signal_agent_segment_id": "rdn:9f1c…:c4a2…", "destinations": [{ "type": "platform", "platform": "the-trade-desk", "account": "agency-seat-1" }], "pricing_option_id": "po_cpm_usd", "idempotency_key": "6d2a7f3e-1c7b-4d2e-9a0f-2b3c4d5e6f70" } } }

Tool results carry the payload both as structuredContent and as JSON text in content[0].text. Every AdCP response carries status, message, timestamp and adcp_version, and echoes your context. Task-level failures return status: "failed" with errors[] (and adcp_error with JSON-pointer issues for validation) and isError: true: VALIDATION_ERROR, SIGNAL_NOT_FOUND, IDEMPOTENCY_CONFLICT, UNSUPPORTED_FEATURE, VERSION_UNSUPPORTED. Protocol failures return JSON-RPC errors (-32001 unauthorized, -32601 unknown method, -32602 unknown tool).

Neutrality. Retailer data is hashed before any activation and is never shared or sold; signals are aggregate segments, never customer records. Every partner operates under the same rules and the same rate card; no retailer, brand or platform receives privileged access to another party's data. Activations and media buys are requests the operator approves and fulfils. To request a partner key, email info@adperical.com with the platform, markets and use case.
Need help? Contact your Adperical account manager or email info@adperical.com for integration support.