Example Question Workflow

This example question workflow is designed to demonstrate one way you can create a question and set up a question run. Many other workflows are possible, but we hope this one will serve as a model you can adapt to your needs.

Create a question

šŸ“˜

Creating questions at the organizational level or the clean room level

This exercise separates out creating the question and provisioning it to a clean room for demonstration purposes. Creating a question at the organizational level lets you use the question as a template for questions in multiple clean rooms.

If you just want to provision a question to a single clean room, you can skip the next two steps by amending the initial POST call to include the cleanroomId in the questionDetails array and the dataTypes object with the required dataset information.

To create a question at the organizational level:

  1. Find the clean room and organization IDs in the UI. For more information, see Where to Find Relevant Metadata.

  2. Create question "q1 - Clone", assigning it to an organization:

    $ cat q1_clone.json
    {
        "questionDetails": {
            "title": "q1 API test",
            "category": "testing",
            "description": "API test question",
            "hasDateParameters": true,
            "hasDateFilter": false,
            "isAnalytics": true,
            "isUserListQuestion": false,
            "isCleanComputeQuestion": false,
            "outputFormat": "REPORT",
            "visualization": "NO_CHART",
            "hasMultipleOutputs": false,
            "queryDetails": [
                {
                    "query": "select count(*) as row_count, \n platform \n from @digital \n group by platform",
                    "cleanRoomType": "Hybrid",
                    "queryLanguage": "SQL"
                }
            ],
            "parameters": [],
            "measures": [
                {
                    "name": "ROW_COUNT",
                    "type": "LONG"
                }
            ],
            "dimensions": [
                {
                    "name": "PLATFORM",
                    "type": "STRING"
                }
            ],
            "userListOutputs": []
        },
        "organizationID": "o1234",
        "settings": {
            "queryValidation": true
        }
    }
    
    
    $ curl -b token -X POST -d @q1_clone.json 'https://api.habu.com/v1/question'
    
    $ {
        "response": {
            "success": true,
            "message": "Question created successfully",
            "questionID": "q1234"
        }
    }
    

    Save the question ID.

Provision the question to a clean room

šŸ“˜

Combining provisioning and dataset assignments

This exercise separates the acts of provisioning questions to a clean room and assigning the question datasets to a clean room for demonstration purposes. In practice, you will probably combine these steps with each other or with the creation of the question.

Provision the question to a clean room with the clean room ID and question ID you saved above.

$ curl -b token -X POST 'https://api.habu.com/v1/cleanrooms/{cleanroomId}/cleanroom-questions?questionId={questionId}&isEditable=true'
$ {
    "response": {
        "id": "a1234",
        "cleanRoomId": "cr1234",
        "ownerOrganizationId": "oo1234",
        "questionId": "5q1234",
        "title": "q1 - Clone",
        "category": "test",
        "stage": "MISSING_DATASETS",
        "status": "ACTIVE",
        "latestRun": {
            "runId": "",
            "status": "RUN_STATUS_UNKNOWN",
            "startTimestamp": "1969-12-31T19:00:00-05:00",
            "endTimestamp": "1969-12-31T19:00:00-05:00"
        },
        "datasetCount": 1,
        "runCount": 0,
        "isEditable": true,
        "questionStatus": "IS_STAGE",
        "questionVersion": 1,
        "displayId": "di1234",
        "description": ""
    }
}

Retrieve question details

It may be useful to retrieve question details for use in the question run you'll set up later. Use the ID for the cleanroomQuestions object you created above.

$curl -b token -X GET 'https://api.habu.com/v1/cleanroom-questions/{cleanroomQuestionsId}'

$ {
    "response": {
        "id": "crq1234",
        "name": "q1 - Clone",
        "displayId": "CRQ-109839",
        "questionType": "ANALYTICAL",
        "category": "test",
        "createdOn": "2025-12-31T19:00:00-05:00",
        "status": "MISSING_DATASETS",
        "dataTypes": {
            "Generic": "Generic"
        },
        "parameters": {},
        "dimension": {
            "PLATFORM": "platform"
        },
        "metrics": {
            "ROW_COUNT": "row_count"
        },
        "cleanroomId": "cr1234",
        "ownerOrganizationId": "oo1234",
        "customerQueryTemplate": "select count(*) as row_count, \n platform \n from @digital \n group by platform"
    }
}

Assign datasets to the clean room

šŸ“˜

The API does not yet support allowing the assignment of datasets on a partner's behalf. For information on enabling this capability for your datasets, see Configuring Datasets in a Clean Room in the UI documentation.

You only need to assign datasets to a clean room once for a given question. After that, the datasets are associated with the clean room unless removed.

To assign datasets:

  1. If necessary, review the list of available datasets and dataset details. For more information, see Fetch a list of all Data Connections.

  2. If necessary, provision the dataset. For more information, see Configure a Cleanroom Dataset by Data Connection ID.

  3. If necessary, assign ownership of the dataset to one of your organizations.

    $ curl -b token -X POST 'https://api.habu.com/v1/cleanroom-questions/{cleanroomQuestionsId}/datasets-ownership'
    
    $ {
        "response": {
            "id": "crq1234",
            "name": "q1 - Clone",
            "displayId": "CRQ-109839",
            "questionType": "ANALYTICAL",
            "category": "test",
            "createdOn": "2025-12-31T19:00:00-05:00",
            "status": "MISSING_DATASETS",
            "dataTypes": {
                "Generic": "Generic"
            },
            "parameters": {},
            "dimension": {
                "PLATFORM": "platform"
            },
            "metrics": {
                "ROW_COUNT": "row_count"
            },
            "cleanroomId": "cr1234",
            "ownerOrganizationId": "oo1234",
            "customerQueryTemplate": "select count(*) as row_count, \n platform \n from @digital \n group by platform"
        }
    }
    
  4. Assign the dataset to the clean room question.

    $ curl -b token -X POST 'https://api.habu.com/v1/cleanroom-questions/{cleanroomQuestionsId}/datasets'
    

Retrieve the question parameters

The question parameters must be included when you execute a question run.

$curl -b token -X GET 'https://api.habu.com/v1/cleanroom-questions/{cleanroomQuestionId}/cleanroom-question-parameters
'
$ {
    "response": {
        "name": "test_deployment_lift_algo - v3",
        "displayId": "CRQ-115299",
        "parameters": [
            {
                "Name": "START_DATE",
                "Index": 0,
                "DataType": "DATE",
                "ParameterType": "RUNTIME_PARAMETER"
            },
            {
                "Name": "END_DATE",
                "Index": 0,
                "DataType": "DATE",
                "ParameterType": "RUNTIME_PARAMETER"
            },
            {
                "Name": "BRAND",
                "Index": 0,
                "DataType": "STRING",
                "ParameterType": "RUNTIME_PARAMETER"
            }
        ]
    }
}

Create a question run

🚧

API Limits

Do not create new question runs more than 20 times per hour.

$ cat question_run_details

$ {
    "response": {
        "name": "API run test",
        "parameters": {
            "digital.exposure_date_utc_start": "YYYY-MM-DD",
            "digital.exposure_date_utc_end": "YYYY-MM-DD",
            "digital.app_id": "bounty"
        }
    }
}
  
$ curl -b token -d @question_run_details -X POST 'https://api.habu.com/v1/cleanroom-questions/{cleanroomQuestionId}/create-run'
  
$ {
    "response": {
        "id": "8e42c214-c610-4841-8b7a-f38210b9e857",
        "name": "API run test",
        "status": "QUEUED",
        "submittedAt": "2025-10-23T19:32:40.891535044Z",
        "completedAt": "",
        "parameters": {},
        "partitionParameters": [
            {
                "digital.exposure_date_utc_start": "YYYY-MM-DD",
                "digital.exposure_date_utc_end": "YYYY-MM-DD",
                "digital.app_id": "bounty"
            }
        ],
        "runMetadata": null,
        "failureReason": null
    }
}

Save the Question Run ID.

Poll the question run status

🚧

API Limits

You can poll the question run status up to every 5 minutes, but we recommend that you poll once an hour.

$ curl -b token -X GET 'https://api.habu.com/v1/cleanroom-question-runs/{cleanroomQuestionRunId}'

$ {
    "response": {
        "id": "8e42c214-c610-4841-8b7a-f38210b9e857",
        "name": "API run test",
        "status": "COMPLETED",
        "submittedAt": "2025-10-23T19:32:40.891535044Z",
        "completedAt": "2025-10-23T21:32:40.891535044Z",
        "parameters": {
            "START_DATE": "2024-10-01",
            "END_DATE": "2025-07-01"
        },
        "partitionParameters": [],
        "runMetadata": null,
        "failureReason": null
    }
}

If the status is COMPLETED, you can download the question run results.

If the status is FAILED, follow the troubleshooting steps at Fetch a Cleanroom Question Run by ID.

Download the question run results

  1. Retrieve a list of the files produced by the question run.
    $ curl -b token -X GET 'https://api.habu.com/v1/cleanroom-question-runs/{cleanroomQuestionRunId}/data'
    
  2. Download the files.
    $ curl -b token -X GET 'https://api.habu.com/v1/cleanroom-question-runs/{cleanroomQuestionRunId}/download/{filename}'