> For the complete documentation index, see [llms.txt](https://docs.kaiko.com/llms.txt). Markdown versions of documentation pages are available by appending `.md` to page URLs; this page is available as [Markdown](https://docs.kaiko.com/on-chain/third-party-data/ncfx/data-on-ramp/request-response-oracle.md).

# Request-response oracle

{% hint style="info" %}
This guide provides step-by-step instructions for creating data requests to the NCFX API using the request-response oracle system on the Canton blockchain. The guide assumes that the RequestFactory contract already exists and focuses on the request creation process using HTTP requests.
{% endhint %}

{% hint style="success" icon="copy" %}
DAR files must be uploaded on your node to use this Oracle [\[Click here to download\]](https://kaiko-delivery-links.s3.us-east-1.amazonaws.com/request-response-oracle-ncfx.zip)
{% endhint %}

## Overview

The request-response oracle enables secure requests for NCFX financial data through the Canton blockchain. The request creation process involves:<br>

1. **Preparing request parameters:** Gather NCFX data request
2. **Retrieving required contracts:** Get Amulet rules and open mining round contracts
3. **Contract exercise:** Submit the transaction to create a data request on the blockchain

<p align="center"></p>

<p align="center"><img src="/files/OCkWigIp9AkWODWQw0lw" alt="" data-size="original"></p>

## Prerequisites

#### Required Information

You need the following information before starting:<br>

* **Canton participant endpoint:** URL of your Canton participant JSON API
* **Canton authentication token:** Valid token for API access
* **Consumer party ID:** Your party identifier in Canton (e.g., Consumer::1220...)
* **RequestFactory contract ID:** Contract ID for the request factory
* **DSO Party ID:** DSO party identifier
* **NCFX arguments:**
  * Data request (stringified JSON)
* **Amulet contract ID:** An Amulet contract for payment
* **Payment locked amount:** Amount to lock for the request

### Environment Setup

Ensure you have:

* HTTP client capability (curl, fetch, requests library, etc.)
* JSON parsing capability
* Access to the Canton blockchain network
* A canton node connected to the same domain as the oracle's node
* Uploaded the Request-Response Oracle DAR file to your canton node
* Sufficient Amulet balance for the request

## Creating a Data Request

### Prepare NCFX Request Parameters

Prepare your NCFX data request as a stringified JSON. Example:

```json
{
    "dataset": "live/midrate/v1/rates",
    "currencypair": "GBPUSD",
    "tenor": "SPT",
    "deliverability": "D",
    "type": "O"
}
```

**Stringify the request (remove newlines and escape quotes):**

{% code overflow="wrap" %}

```
"{\n\"dataset\": \"live/midrate/v1/rates\",\n\"currencypair\": \"GBPUSD\",\n\"tenor\": \"SPT\",\n\"deliverability\": \"D\",\n\"type\": \"O\"\n}"
```

{% endcode %}

**Required Parameters:**

* `dataRequest` → Stringified NCFX data request
* `lockedAmount` → Payment amount to lock
* `dso` → The DSO party
* `amuletRulesCid` → The official amulet rules current contract ID
* `openRoundCid` → The current OpenMiningRound contract ID
* `inputs` → A list of amulets YOUR\_CONSUMER\_PARTY owns

### Submit Request Creation Transaction

Make a POST request to Canton to exercise the CreateRequest choice:

**Request:**

{% code overflow="wrap" %}

```
POST {PARTICIPANT_NODE_JSON_API_URL}/v2/commands/submit-and-wait-for-transaction-tree
Authorization: Bearer {CANTON_TOKEN}
Content-Type: application/json

```

{% endcode %}

**Request body:**

{% code overflow="wrap" %}

```json
{
    "actAs": [
        "{YOUR_CONSUMER_PARTY}"
    ],
    "commandId": "create-request-1",
    "commands": [
        {
            "actAs": [
                "{YOUR_CONSUMER_PARTY}"
            ],
            "commandId": "create-request-1",
            "ExerciseCommand": {
                "contractId": "{REQUEST_FACTORY_CONTRACT_ID}",
                "templateId": "3778b498a5b2703be4ab088451b13d3688a81e421c4332cd5e4a0cfa4bd245f1:Request-ResponseOracle.RequestFactory:RequestFactory",
                "choice": "CreateRequest",
                "choiceArgument": {
                    "dso": "{DSO_PARTY}",
                    "amuletRulesCid": "{AMULET_RULES_CONTRACT_ID}",
                    "openRoundCid": "{OPEN_ROUND_CONTRACT_ID}",
                    "inputs": [
                        {
                            "tag": "InputAmulet",
                            "value": "{YOUR_AMULET_CONTRACT_ID}"
                        }
                    ],
                    "dataRequest": "{STRINGIFIED_NCFX_DATA_REQUEST}",
                    "lockedAmount": {PAYMENT_LOCKED_AMOUNT}
                }
            }
        }
    ],
    "disclosedContracts": [
        {
            "templateId": "{AMULET_PACKAGE_ID}:Splice.AmuletRules:AmuletRules",
            "contractId": "{AMULET_RULES_CONTRACT_ID}",
            "synchronizerId": "{GLOBAL_DOMAIN_SYNCHRONIZER_ID}",
            "createdEventBlob": "{AMULET_RULES_DISCLOSED_BLOB}"
        },
        {
            "templateId": "{AMULET_PACKAGE_ID}:Splice.Round:OpenMiningRound",
            "contractId": "{OPEN_ROUND_CONTRACT_ID}",
            "synchronizerId": "{GLOBAL_DOMAIN_SYNCHRONIZER_ID}",
            "createdEventBlob": "{OPEN_ROUND_DISCLOSED_BLOB}"
        }
    ]
}


```

{% endcode %}

The exerciseResult part in the response looks like:

{% code overflow="wrap" %}

```json
{
    "transferResult": {
        "round": {
            "number": "29450"
        },
        "summary": {
            "inputAppRewardAmount": "0.0000000000",
            "inputValidatorRewardAmount": "0.0000000000",
            "inputSvRewardAmount": "0.0000000000",
            "inputAmuletAmount": "267332873.0096101736",
            "balanceChanges": [
                [
                    "kaiko-devnet-1::12200fd571073e933e107afec72902c1a59f4f0ec3be8ef4ada5231c9d3f6d93de2c",
                    {
                        "changeToInitialAmountAsOfRoundZero": "3.4415328900",
                        "changeToHoldingFeesRate": "0.0001168602"
                    }
                ]
            ],
            "holdingFees": "0.0000000000",
            "outputFees": [
                "0.0000000000"
            ],
            "senderChangeFee": "0.0000000000",
            "senderChangeAmount": "267332868.0096101736",
            "amuletPrice": "0.1628090000",
            "inputValidatorFaucetAmount": "0.0000000000",
            "inputUnclaimedActivityRecordAmount": "0.0000000000"
        },
        "createdAmulets": [
            {
                "tag": "TransferResultLockedAmulet",
                "value": "00ed9e1b4151e1ef755c6299e13daaaf9a4db647e276d531450458afa8379e5a62ca121220ae6ee4724b56f56318093df7cb1eee5e6d8caa35d3ad37ce0b914b02eb86ddff"
            }
        ],
        "senderChangeAmulet": "00dfd2b1bcc153d42ee241a2ac6c524bcc2c5ae5ee0894b918b3bb302b6fc9a173ca1212204132fe27f6d8f1c0531d0ad0bdf80a36828fcf3598baceef12d4b9696da7ebc8",
        "meta": {
            "values": {
                "splice.lfdecentralizedtrust.org/sender": "kaiko-devnet-1::12200fd571073e933e107afec72902c1a59f4f0ec3be8ef4ada5231c9d3f6d93de2c",
                "splice.lfdecentralizedtrust.org/tx-kind": "transfer"
            }
        }
    },
    "requestCid": "00ac8fef95efed0583d531ac51ca1eeb2881d1c31f87b31e3ff321eb5441d619e3ca1212206c3d1b48abb01cb7ce28af486debeb9739ec6e8668fec4b19af6255e610fccbb",
    "oracleResponse": "Request created for: {\n\"dataset\": \"live/midrate/v1/rates\",\n\"currencypair\": \"GBPUSD\",\n\"tenor\": \"SPT\",\n\"deliverability\": \"D\",\n\"type\": \"O\"\n} (escrowed 5.0 Amulet until 2026-02-04T15:05:03.617409Z)",
    "lockedAmount": "5.0000000000",
    "requestId": "babc4f56d892"
}
```

{% endcode %}

## Troubleshooting

A request can fail. In this case, the request contract is archived, and a FailedRequest is created. Here are the failing scenarios:

### **1. The locked amount does not cover the service amount**

* **Cause**: The lockedAmount you specified is less than the service fee charged by the oracle
* **Solution:**
  * Increase the `lockedAmount` parameter in your request
  * Ensure you have sufficient Amulet balance

### **2. Stringified JSON request Data Is Not Valid**

* **Cause:** The `dataRequest` parameter is not a properly stringified JSON string
* **Solution:**
  * Verify your JSON is valid before stringifying
  * Ensure all quotes are properly escaped (\\")
  * Check that newlines are removed or escaped
  * Test your JSON with a validator before stringifying

### **3. NCFX Returns an Error When Sending the Request**

* **Cause:** NCFX's API returned an error when processing your request
* **Solution:**
  * Check NCFX API status and availability
  * Verify your data request format matches NCFX's API requirements
  * Ensure requested fields and instruments are available in your catalog
  * Check NCFX rate limits haven't been exceeded
  * Review NCFX's API documentation for valid request formats

## Reading the FilledRequest

### Request processing

The oracle

* detects your new request
* proceeds to the payment by unlocking your amulet, taking the service amount needed from it and sending back to the consumer party the remaining amount
* reads the Request contract
* makes the request to NCFX on your behalf
* archives the Data Request contract and writes the response data into a FilledRequest which fields look like:

{% code overflow="wrap" %}

```json
{
    "oracle": "{ORACLE_PARTY}",
    "consumer": "{CONSUMER_PARTY}",
    "requestData": "{stringified json request data}",
    "responseData": "{stringified json response data}",
    "serviceAmountCharged": "1.0000000000"
}
```

{% endcode %}

### Read the `FilledRequest` contract

Once the oracle has processed your request and received a response from NCFX, a FilledRequest contract is created. To retrieve it, you need to query the active contracts on the Canton ledger.

**Query for FilledRequest contracts**

Make a POST request to retrieve your FilledRequest contracts.

**Request:**

{% code overflow="wrap" %}

```
POST {PARTICIPANT_NODE_JSON_API_URL}/v2/state/active-contracts
Authorization: Bearer {CANTON_TOKEN}
Content-Type: application/json
```

{% endcode %}

**Request body:**

{% code overflow="wrap" %}

```json
{
    "activeAtOffset": {offset},
    "verbose": false,
    "eventFormat": {
        "filtersByParty": {
            "{YOUR_CONSUMER_PARTY}": {
                "cumulative": [
                    {
                        "identifierFilter": {
                            "TemplateFilter": {
                                "value": {
                                    "includeCreatedEventBlob": false,
                                    "templateId": "3778b498a5b2703be4ab088451b13d3688a81e421c4332cd5e4a0cfa4bd245f1:Request-ResponseOracle.Request:FilledRequest"
                                }
                            }
                        }
                    }
                ]
            }
        },
        "verbose": false
    }
}
```

{% endcode %}

**Expected response:**

{% code overflow="wrap" %}

```json
[
    {
        "workflowId": "",
        "contractEntry": {
            "JsActiveContract": {
                "createdEvent": {
                    "offset": 627962,
                    "nodeId": 1,
                    "contractId": "{FILLED_REQUEST_CONTRACT_ID}",
                    "templateId": "3778b498a5b2703be4ab088451b13d3688a81e421c4332cd5e4a0cfa4bd245f1:Request-ResponseOracle.Request:FilledRequest",
                    "contractKey": null,
                    "createArgument": {
                        "oracle": "{ORACLE_PARTY}",
                        "consumer": "{YOUR_CONSUMER_PARTY}",
                        "requestId": "babc4f56d892",
                        "requestData": "{stringified json request data}",
                        "responseData": "{stringified json response data}",
                        "serviceAmountCharged": "1.0000000000"
                    },
                    "createdEventBlob": "",
                    "interfaceViews": [],
                    "witnessParties": [
                        "{ORACLE_PARTY}"
                    ],
                    "signatories": [
                        "{ORACLE_PARTY}"
                    ],
                    "observers": [
                        "{YOUR_CONSUMER_PARTY}"
                    ],
                    "createdAt": "2026-01-23T14:56:45.708037Z",
                    "packageName": "Request-Response-oracle",
                    "representativePackageId": "3778b498a5b2703be4ab088451b13d3688a81e421c4332cd5e4a0cfa4bd245f1",
                    "acsDelta": true
                },
                "synchronizerId": "global-domain::1220...",
                "reassignmentCounter": 0
            }
        }
    }
]
```

{% endcode %}

## **Understanding the response**

The response contains the FilledRequest contract with the following important fields in createArgument:

**Contract fields:**

<table data-header-hidden><thead><tr><th width="373.71875"></th><th></th></tr></thead><tbody><tr><td><code>oracle</code></td><td>The oracle party that processed the request</td></tr><tr><td><code>consumer</code></td><td>Your consumer party ID</td></tr><tr><td><code>requestId</code></td><td>NCFX request ID</td></tr><tr><td><code>requestData</code></td><td>The original request data you submitted (stringified JSON)</td></tr><tr><td><code>responseData</code></td><td>NCFX's response data (stringified JSON)</td></tr><tr><td><code>serviceAmountCharged</code></td><td>The actual service fee charged by the oracle</td></tr></tbody></table>

**Service amount:** The `serviceAmountCharged` shows the actual fee deducted from your locked amount. Any remaining amount from your locked amount is returned to you.


---

# 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 dynamically by asking a question.

Perform an HTTP GET request on the current page URL with the `ask` query parameter, and the optional `goal` query parameter:

```
GET https://docs.kaiko.com/on-chain/third-party-data/ncfx/data-on-ramp/request-response-oracle.md?ask=<question>&goal=<endgoal>
```

`ask` is the immediate question: it should be specific, self-contained, and written in natural language.
`goal` is optional and describes the broader end goal you are ultimately trying to accomplish on behalf of the user. GitBook uses it to tailor the answer towards what is most useful for that goal.

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.
