Computation requests
An Advanced Analytics DCR that was published as interactive lets a participant request a computation that was not part of the original configuration. The requested computation becomes runnable once the data owners of every dataset it reads have approved it and the request has been integrated. The SDK offers the same lifecycle as the Requests tab in the web app, so you can automate review and approval across many data clean rooms.
The lifecycle is: submit → approve (once per affected dataset) → integrate. A request can be rejected at any point before it is integrated, which ends it for good.
Requests only exist on interactive DCRs. Building a request against an immutable DCR fails immediately, because its participants have no permission to merge a change into it.
Submitting a request
Build the requested computation with the same node definition classes used to build a DCR from scratch, then submit it:
import decentriq_platform as dq
from decentriq_platform.analytics import PythonComputeNodeDefinition
client = dq.create_client("@@ YOUR EMAIL HERE @@", "@@ YOUR TOKEN HERE @@")
dcr = client.retrieve_analytics_dcr("@@ DCR ID @@")
request = (
dcr.build_request()
.add_computation(
PythonComputeNodeDefinition(
name="revenue_by_segment",
script="...",
dependencies=["customers"],
)
)
.add_participant("analyst@example.com", analyst_of=["revenue_by_segment"])
.with_note("Breaks revenue down by segment for the Q3 review.")
.submit()
)
A few constraints are worth knowing before you build one:
- A request adds exactly one computation. Submit one request per computation you want to add.
- Only computations can be requested. Data nodes are part of the DCR itself and must be present when it is created.
add_participantcan only grant the analyst permission, and only on the computation the request adds. A request cannot grant data owner permissions.with_notetravels inside the configuration commit, so every approver sees it when reviewing.
Submitting doesn't approve or integrate the request, even when the submitting user is a required approver.
Reviewing requests
for request in dcr.get_submitted_requests():
print(request.id, request.status, request.owner)
get_submitted_requests returns the requests that are still pending plus those that were rejected — the latter so the user who submitted one can read why it was turned down. Requests that have already been integrated are not included; use dcr.get_commits() to see those. A single request can be fetched with dcr.get_submitted_request(request_id).
SubmittedRequest.status is one of:
| Status | Meaning |
|---|---|
pending | At least one affected dataset has not been approved by any of its data owners |
ready_to_integrate | Every affected dataset has been approved; the request can be integrated |
integrated | The request has been merged into the DCR configuration |
rejected | The request was turned down and can no longer be approved or integrated |
superseded | The request was rebased and replaced by a new one |
Two properties describe what a request still needs:
approver_groups— one group per dataset the requested computation reads, made up of that dataset's data owners. Any one member of a group can approve on the group's behalf. A user who owns several affected datasets covers all of their groups with a single approval.missing_approvals()— the groups none of whose members has approved yet. Empty means the request can be integrated.
Both are re-read from the platform on each access, so an approval given by another participant since the object was built is taken into account. Both raise once the request has settled, since the approval rule can no longer be evaluated for it.
Use request.scope to inspect what is actually being requested before approving: the computation, its dependencies, and the analysts it would grant access to.
Approving
request.approve()
Only the users in request.required_approvers can approve. Approving again after the current user has already approved does nothing, so the call is safe to run over many data clean rooms in a loop.
Rejecting
request.reject("Reads the raw email column; aggregate it first.")
A reason is required: it is the only thing that tells the requester what to change. They read it here on request.rejection, and in the web app on the request's Rejected chip.
Both the required approvers and the user who submitted the request may reject it, the latter amounting to withdrawing it. Withdrawing is SDK-only: the web app shows Reject to the required approvers alone.
A rejection is final. One approver rejecting closes the request for everyone, even where another owner of the same dataset could still have approved it. It cannot be approved, integrated or reopened afterwards; a replacement has to be submitted as a new request.
On a rejected request, request.rejection carries the reason and the rejected_by email. Rejecting an already-rejected request does nothing.
Integrating
request.integrate()
Every affected dataset must have been approved by one of its data owners first. Any participant of the DCR can integrate, not just an approver. Integrating an already-integrated request does nothing; integrating a rejected or superseded one raises.
Once integrated, the computation is available to the analysts listed on the request and visible to all participants. Run it like any other computation, see Run computations and export data.
End-to-end example
Approve and integrate every request that is waiting on the current user, across all their Analytics DCRs:
import decentriq_platform as dq
from decentriq_platform.analytics import RequestStatus
me = client.user_email
for description in client.get_data_room_descriptions(exclude_stopped_dcrs=True):
if description["kind"] != dq.types.DataRoomKind.DATA_SCIENCE:
continue
dcr = client.retrieve_analytics_dcr(description["id"])
for request in dcr.get_submitted_requests():
# `approve` raises for a user who is not a required approver, and
# `missing_approvals` raises once a request has settled.
if request.status != RequestStatus.PENDING or me not in request.required_approvers:
continue
print(f"{dcr.id}: {request.owner} requested {request.scope.computation.name}")
request.approve()
if not request.missing_approvals():
request.integrate()
approve and integrate are both no-ops when the work is already done, so this loop is safe to re-run.