# Request-response oracle

{% hint style="info" %}
This guide provides step-by-step instructions for creating data requests to the Bloomberg DL Per Security 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/prepaid-oracle-bloomberg-0.0.2.zip)
{% endhint %}

## Overview

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

1. **Preparing request parameters:** Gather Bloomberg 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>

<figure><img src="https://1969223162-files.gitbook.io/~/files/v0/b/gitbook-x-prod.appspot.com/o/spaces%2F8j8Uy5gOsywos3YYkZNv%2Fuploads%2FypTuSU8bM5ZsV5GD7lDk%2FEntitlement%20Approval-2026-03-19-132122.png?alt=media&#x26;token=ad6dcec2-67ec-4e6e-b50d-d40effc9b777" alt=""><figcaption></figcaption></figure>

## 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
* **Bloomberg arguments:**
  * Data Request (stringified JSON)
  * Catalog (Account ID)
  * Credentials (client\_id, client\_secret)
* **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 **Bloomberg** Request Parameters

Prepare your **Bloomberg** data request as a stringified JSON. Example:

```json
{
    "@type": "DataRequest",
    "name": "TreasuryRequestIBVAL",
    "description": "Treasury Last IBVAL",
    "universe": {
        "@type": "Universe",
        "contains": [
    {
            "@type": "Identifier",
            "identifierType": "TICKER",
            "identifierValue": "FRTR 3.5 11/25/2035 144A Govt"
        }]
    },
    "fieldList": {
        "@type": "DataFieldList",
        "contains": [{"mnemonic": "PX_LAST"}]
    },
    "pricingSourceOptions": {
        "@type": "DataPricingSourceOptions",
        "prefer": { "mnemonic": "IBVL" },
        "exclusive": true
    },
    "trigger": { "@type": "SubmitTrigger"},
    "formatting": {
        "@type": "MediaType",
        "outputMediaType": "application/json"
    }
}

```

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

{% code overflow="wrap" %}

```
"{\"@type\":\"DataRequest\",\"name\":\"TreasuryRequestIBVAL\",\"description\":\"TreasuryLastIBVAL\",\"universe\":{\"@type\":\"Universe\",\"contains\":[{\"@type\": \"Identifier\",\"identifierType\": \"TICKER\",\"identifierValue\": \"FRTR 3.5 11/25/2035 144A Govt\"}]},\"fieldList\":{\"@type\":\"DataFieldList\", \"contains\":[{\"mnemonic\":\"PX_LAST\"}]},\"pricingSourceOptions\":{\"@type\":\"DataPricingSourceOptions\",\"prefer\":{\"mnemonic\":\"IBVL\"},\"exclusive\":true},\"trigger\":{\"@type\":\"SubmitTrigger\"},\"formatting\":{\"@type\":\"MediaType\",\"outputMediaType\": \"application/json\"}}"

```

{% endcode %}

**Required Parameters:**

* `dataRequest` → Stringified **Bloomberg** 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}"
                        }
                    ],
            			  "catalog": "{CATALOG_ID}",
            			  "clientId": "{CLIENT_ID}",
            			  "clientSecret": "{CLIENT_SECRET}",
                    "dataRequest": "{STRINGIFIED_BLOOMBERG_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: ..... (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. Bloomberg Returns an Error When Sending the Request**

* **Cause:** Bloomberg's API returned an error when processing your request
* **Solution:**
  * Check Bloomberg API status and availability
  * Verify your data request format matches **Bloomberg**'s API requirements
  * Ensure requested fields and instruments are available in your catalog
  * Check Bloomberg rate limits haven't been exceeded
  * Review Bloomberg'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 Bloomberg 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 Bloomberg, 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><strong>Bloomberg</strong> 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><strong>Bloomberg</strong>'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.
