Scenario: Sending Structured Data in a Custom Format using HTTPs

Use this scenario to send and monitor structured data from a device in a custom format. If a device sends data in a custom format, that means the device cannot be programmed to change its output.

In this case, define the custom digital twin adapter mapping, to translate the incoming data into a format recognized by the IoT Platform.

An administrator must add a policy to your tenancy and the compartments you want to use. For policy examples and prerequisites, see Policy Details for the Internet of Things (IoT) Platform and IoT Prerequisites.

Step 1: Create an IoT Domain Group and an IoT Domain

Use an existing IoT domain group and IoT domain or create an IoT domain group and create an IoT domain to use for this scenario.

After you have the IoT domain group and IoT domain you want to work, follow these steps to set up digital twin resources to receive structured data in a custom format from a device. All IoT resources must be in the same region.

To get the <domain-short-id-from-device-host> for the IoT domain or to get the IoT domain OCID, you can get the details for the IoT domain you want to work with.

Step 2: Create a Digital Twin Model

  1. Create specifications using the Digital Twins Definition Language (DTDL) for your digital twin model similar to this example with the data you want to collect.

    To use this example, save this code snippet as a digital-twin-model.json file and then reference this file in the next step when you create a digital twin model.

    A digital twin model requires a Digital Twin Model Identifier (DTMI) as a unique identifier. For example: dtmi:com:oracle:example:hvac;1

    {
      "@context": [
        "dtmi:dtdl:context;3"
      ],
      "@id": "dtmi:com:oracle:example:hvac;1",
      "@type": "Interface",
      "displayName": "HVAC",
      "description": "A digital twin model for HVAC",
      "contents": [
        {
          "@type": "Telemetry",
          "name": "temperature",
          "schema": "integer"
        },
        {
          "@type": "Telemetry",
          "name": "humidity",
          "schema": "integer"
        },
        {
          "@type": "Property",
          "name": "power",
          "schema": "boolean"
        },
        {
          "@type": "Property",
          "name": "batteryLevel",
          "schema": "integer"
        }
      ]
    }
  2. Use the oci iot digital-twin-model create command with the required parameter to create a digital twin model. Replace the <iot-domain-OCID> with the OCID for the IoT domain you want to associate to this digital twin model. Reference the digital-twin-model.json file created in the previous step or a specifications file for your specific scenario. For more information about referencing files, see Using a JSON File for Complex Input:
    oci iot digital-twin-model create --iot-domain-id <iot-domain-OCID> --spec file://digital-twin-model.json
    Example response using the digital-twin-model.json file from the previous step. This example response shows the DTMI URI and the IoT digital twin model OCID:
    {
      "data": {
        "defined-tags": {
          "Oracle-Tags": {
            "CreatedBy": "default/user@oracle.com",
            "CreatedOn": "2025-09-11T05:56:50.514Z"
          }
        },
        "description": "A digital twin model for HVAC",
        "display-name": "HVAC",
        "freeform-tags": {},
        "id": "<iot-digital-twin-model-OCID>",
        "iot-domain-id": "<iot-domain-OCID>",
        "lifecycle-state": "ACTIVE",
        "spec-uri": "dtmi:com:oracle:example:hvac;1",
        "system-tags": {},
        "time-created": "2025-09-11T05:56:50.587000+00:00",
        "time-updated": "2025-09-11T05:56:50.587000+00:00"
      },
      "etag": "<unique-id>"
    }
    

Step 3: Create a Digital Twin Adapter for a Custom Format

If a device sends data in a custom format, that means the device cannot be programmed to change its output. In this case, define the custom format in the digital twin adapter mapping,

To do that, define the inbound-envelope and inbound-routes in a JSON format to convert the data to a format you want to work with in Oracle services and applications so you can connect to view your IoT data in different systems.

Using the CLI

  1. Define an inbound envelope file://inbound-envelope.json to specify how to extract metadata from the device's payload. To extract the value at $.time from the incoming JSON and map it to timeObserved field in the digital twin model. These mappings allow you to extract specific metadata such as timestamps using JQ expressions.

    This example shows the device endpoint, a sample payload, and an inbound envelope mapping that applies JQ expressions to extract or reshape the data.

    {
      "reference-endpoint": "telemetry/health",
      "reference-payload": {
        "dataFormat": "JSON",
        "data": {
          "time": "<timestamp>",
          "data": {
            "temp": 0,
            "hum": 0,
            "power": false,
            "batteryLevel": 0
          }
        }
      },
      "envelope-mapping": {
        "timeObserved": "$.time"
      }
    }
  2. Create a file://inbound-routes.json file to define how specific payload data from device messages is mapped and routed to your digital twin instance.

    Each route includes a condition matching the endpoint, a reference payload structure, and mapping instructions for transferring or transforming values.

    This example shows 2 payload conditions defined.
    Note

    The maximum inbound routes allowed is 128.
    [
      {
        "condition": "${endpoint(2) == \"heartbeat\"}",
        "reference-payload": {
          "data": {
            "temp": 75,
            "hum": 62
          }
        },
        "payload-mapping": {
          "$.temperature": "$.data.temp",
          "$.humidity": "${.data.hum - 5}"
        }
      },
      {
        "condition": "${endpoint(2) == \"health\"}",
        "reference-payload": {
          "data": {
            "power": false,
            "batteryLevel": 60
          }
        },
        "payload-mapping": {
          "$.power": "$.data.power",
          "$.batteryLevel": "${.data.batteryLevel - 5}"
        }
      }
    ]
  3. Use the oci iot digital-twin-adapter create command to create a digital twin adapter. Replace the <iot-domain-OCID> with the OCID for your IoT domain, and replace the <dtmi:com:oracle:example:hvac> with the DTMI URI with the DTMI URI for the digital twin model you want to associate to this digital twin adapter.

    The following example uses the DTMI URI parameter to associate the digital twin model. Alternatively, you can use the --digital-twin-model-id parameter with the <digital-twin-model-OCID> for the digital twin model you want to associate to this digital twin adapter.

    Reference the files file://inbound-enevelop.json and file://inbound-enevelop.json created in the previous step.
    oci iot digital-twin-adapter create --iot-domain-id <iot-domain-OCID> --digital-twin-model-spec-uri 'dtmi:com:oracle:example:hvac;1' --inbound-envelope <file://inbound-enevelop.json> --inbound-routes <file://inbound-routes.json>
    This example response shows a custom payload mapping defined in the --inbound-envelope and the --inbound-routes options, by referencing json files, and a specific digital twin adapter OCID that's associated with a specific digital twin model with a unique DTMI URI and digital twin model OCID:

    dtmi:com:oracle:example:core:hvac:sp;1

    {
      "data": {
        "defined-tags": {
          "Oracle-Tags": {
            "CreatedBy": "default/user@oracle.com",
            "CreatedOn": "2025-09-11T06:09:27.323Z"
          }
        },
        "description": null,
        "digital-twin-model-id": "<iot-digital-twin-model-OCID>",
        "digital-twin-model-spec-uri": "dtmi:com:oracle:example:hvac;1",
        "display-name": "<digital-twin-adapter-display-name>",
        "freeform-tags": {},
        "id": "<iot-digital-twin-adapter-OCID>",
        "inbound-envelope": {
          "envelope-mapping": {
            "time-observed": "$.time"
          },
          "reference-endpoint": "telemetry/health",
          "reference-payload": {
            "data": {
              "time": "2025-09-11T06:09:27.878106Z"
            },
            "data-format": "JSON"
          }
        },
        "inbound-routes": [
          {
            "condition": "${endpoint(2) == \"heartbeat\"}",
            "description": null,
            "payload-mapping": {
              "$.humidity": "${.hum-1}",
              "$.temperature": "$.temp"
            },
            "reference-payload": {
              "data": {
                "hum": 62,
                "temp": 75
              },
              "data-format": "JSON"
            }
          },
          {
            "condition": "${endpoint(2) == \"health\"}",
            "description": null,
            "payload-mapping": {},
            "reference-payload": {
              "data": {
                "batteryPercentage": 60,
                "on": false
              },
              "data-format": "JSON"
            }
          }
        ],
        "iot-domain-id": "<iot-domain-OCID>",
        "lifecycle-state": "ACTIVE",
        "system-tags": {},
        "time-created": "2025-09-30T19:55:36.293000+00:00",
        "time-updated": "2025-09-30T19:55:36.293000+00:00"
      },
      "etag": "<unique-id>"
    }
    

Step 4: Create a Digital Twin Instance with the Adapter

Using the CLI

Use the oci iot digital-twin-instance create and the <iot-domain-OCID> and the <certificate-or-secret-OCID> required parameters to create a digital twin instance. Replace the <certificate-or-secret-OCID> with the vault secret OCID you want use and replace <iot-domain-OCID> with the OCID for the IoT domain for your environment.

If the digital twin instance is set up to receive device data, then you must use the authentication ID parameter with a vault secret or certificate OCID, so the digital twin can authenticate. To do that create a secret or create a certificate in the same region and tenancy as any other related IoT resources.

This example uses the --display-name option, replace <your-digital-twin-instance-name> with a user-friendly name for your digital twin instance.

Replace the <digital-twin-adapter-OCID> with the digital twin adapter created in the previous step.
oci iot digital-twin-instance create --iot-domain-id <iot-domain-OCID> --display-name <your-digital-twin-instance-name> --auth-id <certificate-or-secret-OCID> --digital-twin-adapter-id <digital-twin-adapter-OCID>
This example response shows the digital twin adapter and DTMI URI defined for the digital twin model associated with this digital twin instance.

dtmi:com:oracle:example:hvac;1

{
  "data": {
    "auth-id": "<vault-secret-OCID>",
    "defined-tags": {
      "Oracle-Tags": {
        "CreatedBy": "default/user@oracle.com",
        "CreatedOn": "2025-08-14T17:41:11.973Z"
      }
    },
    "description": null,
    "digital-twin-adapter-id": "<iot-digital-twin-adapter-OCID>",
    "digital-twin-model-id": "<digital-twin-model-OCID>",
    "digital-twin-model-spec-uri": "dtmi:com:oracle:example:hvac;1",
    "display-name": "<your-digital-twin-instance-name>",
    "external-key": "<digital-twin-instance-external-key>",
    "freeform-tags": {},
    "id": "<digital-twin-instance-OCID>",
    "iot-domain-id": "<iot-domain-OCID>",
    "lifecycle-state": "ACTIVE",
    "system-tags": {},
    "time-created": "2025-08-14T17:41:13.638000+00:00",
    "time-updated": "2025-08-14T17:41:13.638000+00:00"
  },
  "etag": "<unique-id>"
}

Step 5: Send Telemetry Data

Use the following curl command and the <digital-twin-instance-external-key> to send or post data. For more information, see Using cURL.

Depending on the data type you want to send, use the following example to send telemetry:
curl -u 'digital-twin-instance-external-key:device-password' -H "content-type: application/json" https://<domain-short-id-from-device-host>.device.iot.<region>.oci.oraclecloud.com/telemetry/heartbeat -d '{"temp": 70, "hum": 55}'
Accepted%
curl -i -X POST \
     -u "<digital-twin-instance-external-key>:<device-password>" \
     -H "Content-Type: application/json" \
     "https://<domain-short-id-from-device-host>.device.iot.<region>.oci.oraclecloud.com/telemetry/heartbeat" \
     -d '{
           "digital_twin_instance_id": "<iot-digital-twin-instance-OCID>",
           "received_at": "2024-08-14T06:01:30.432829Z",
           "endpoint": "telemetry/heartbeat",
           "content_type": "application/json",
           "content": {
             "time": "<time>",
             "data": {
               "temp": 70,
               "hum": 65
             }
           }
         }'

Step 6: View Telemetry Data

Use the Internet of Things Data API to Get the Data

If you configured access your IoT data using ORDS and you have the required authentication token, then you can use the Internet of Things Data API to get the data that you want to monitor.

This example uses HTTPs. Alternatively, you can use MQTT and MQTT over WebSockets. For specific examples, see Scenarios.

Replace the variables with the values for your environment:
curl -H "Authorization: Bearer <token>" \
     -X GET "https://<domain-group-short-id>.data.iot.<region>.oci.oraclecloud.com/ords/<domain-short-id>/20250531/rawData?q={\"$and\":[{\"digital_twin_instance_id\":\"<iot-digital-twin-OCID>\"}]}"

Use SQL statements to View your Data

If you configured access your view your data directly in the database or if you configured access to view your data in APEX, then you can use this SQL statement to view raw telemetry data:
select * from <domain-short-id-from-device-host>__IOT.RAW_DATA where digital_twin_instance_id = '<iot-digital-twin-OCID>';
Note

Notice the schema name contain two underscores: __IOT

Or use this SQL statement to view the rejected telemetry data. Replace the <domain-short-id-from-device-host> with the domain short ID for your IoT domain and the <iot-digital-twin-OCID> with the digital twin's OCID that you want to view rejected data from:

select * from <domain-short-id-from-device-host>__IOT.REJECTED_DATA where digital_twin_instance_id = '<iot-digital-twin-OCID>';
Or use this SQL statement to view the historized telemetry data. Replace the <domain-short-id-from-device-host> with the domain short ID for your IoT domain and the <iot-digital-twin-OCID> with the OCID for the digital twin you want to view rejected data from:
select * from <domain-short-id>__IOT.DIGITAL_TWIN_HISTORIZED_DATA where digital_twin_id = '<digital-twin-OCID>';