Skip to content

Datasets_ResolvePoses

POST
/v1/accounts/{account}/datasets/{dataset}:resolvePoses
curl --request POST \
--url https://dev-api.qualiastudios.dev/v1/accounts/example/datasets/example:resolvePoses \
--header 'Content-Type: application/json' \
--header 'X-API-Key: <X-API-Key>' \
--data '{ "name": "example", "posesAt": "POSES_AT_UNSPECIFIED", "calibration": { "filename": "example", "deriveDate": "example", "hands": [ { "side": "SIDE_UNSPECIFIED", "trackerToTcp": { "xyz": [ 1 ], "rpy": [ 1 ] }, "eeToTcp": { "xyz": [ 1 ], "rpy": [ 1 ] } } ], "eeLink": "example" }, "mergedSettingsContext": { "mergedDataset": "example", "members": [ "example" ], "membershipRevision": "example", "etag": "example", "sourceCommit": "example" }, "requestId": "example", "validateOnly": true, "members": [ "example" ], "membershipRevision": "example", "expectedOperationId": "example" }'

Resolves a handheld upload’s poses at each hand’s TCP (poses_at TCP), or undoes that (ROOT), by re-deriving the upload with the calibration. It adds the <side>.tcp.* channels beside the gripper root’s <side>.ee.*, which stay exactly as they were.

The work is accepted, not finished: it is a new publication of the whole upload, as long as the upload’s video takes to check again, so the response says only that it started. Poll it through the sync-status route (GET /datasets/sync-status/{operation_id}) until it is finalized or failed; GetDataset then shows the new poses_at. The old version stays readable, and a version tag or pin cut earlier keeps the poses it had. Resolving again with a replaced calibration is the same call; the same calibration again, or ROOT on an upload not at the TCP, changes nothing and answers no_op.

One re-derive of an upload runs at a time (this call, ReingestDataset and the dashboard’s automatic resolve after a robot is picked all admit one). The same resolve asked while it runs answers the running operation, which is the one to poll; a different one (an undo of it, another calibration) is FAILED_PRECONDITION until it lands. There is no way to cancel one.

Only the poses at the TCP are stored. What reads them is not part of this call: a reach check still walks the gripper root, and an export carries <side>.ee.* as before.

FAILED_PRECONDITION unless the dataset is a LeRobot upload recorded on a handheld rig whose state ingest converted (or a raw MCAP recording with tracked poses); also on a mixture, a merged dataset (QUA-3511: every upload of a merge sits at one frame, so resolve them before they merge) and an upload merged into one (naming it). The re-derive is built on the version that was current when it started, so it fails (the status route says so) when a review ruling or a trim lands first: resolve again.

account
required
string

The account id.

dataset
required
string

The dataset id.

Media type application/json

Request for Datasets.ResolvePoses.

object
name
required

Accounts/{account}/datasets/{dataset}

string
posesAt
required

TCP to resolve at the TCP (again, with a replaced calibration, when the upload is there already), ROOT to undo that. Unspecified is INVALID_ARGUMENT. (— api-linter: core::0140::prepositions=disabled aip.dev/not-precedent: the same name as Dataset.poses_at. —)

string format: enum
Allowed values: POSES_AT_UNSPECIFIED ROOT TCP
calibration

The calibration to resolve with: required for TCP, empty for ROOT. The caller supplies it, normally the calibration of the rig the upload was recognised as recorded on (Dataset.poses_rig; not a dataset label, those are editable, and not Episode.rig, which ingest may not have used), and it is recorded in the upload’s meta/ingest.toml, so the upload does not follow the rig afterwards. Both hands, every number finite. INVALID_ARGUMENT otherwise.

object
filename
required

The uploaded file’s name, e.g. “tcp_calibration.json”.

string
deriveDate

When the calibration was made, “YYYY-MM-DD”: the leading date of the file’s calibrated or else its derived text. Empty when the file gives none.

string
hands
required

Both hands, left then right.

Array<object>

One hand’s transform.

object
side
required

Which hand.

string format: enum
Allowed values: SIDE_UNSPECIFIED SIDE_LEFT SIDE_RIGHT
trackerToTcp
required

The TCP’s pose in the tracker’s frame: xyz in metres and rpy in radians (URDF convention, extrinsic XYZ), the file’s rotation matrix converted. A rotation whose pitch is within a hair of +-90 degrees has no unique roll and yaw; it is stored with roll 0. (— api-linter: core::0140::prepositions=disabled aip.dev/not-precedent: a transform is named by its direction, as the calibration file’s own controller_to_tcp is. —)

object
xyz

X, y, z in metres.

Array<number>
rpy

Roll, pitch, yaw in radians.

Array<number>
eeToTcp

The same TCP in the frame of the gripper design’s ee_link for this hand, in the form of tracker_to_tcp (so the same pitch limit). Display only. Set with ee_link or not at all, and only when it composes with the file’s controller_to_ee to the file’s controller_to_tcp, which is what makes the TCP drawn the TCP used. (— api-linter: core::0140::prepositions=disabled aip.dev/not-precedent: a transform is named by its direction, as the calibration file’s own ee_to_tcp is. —)

object
xyz

X, y, z in metres.

Array<number>
rpy

Roll, pitch, yaw in radians.

Array<number>
eeLink

The link of the rig’s gripper design that Hand.ee_to_tcp is a pose in, with {side} for “left” or “right” (the gripper joint’s convention in RobotModel.Gripper), e.g. “openarm_{side}_ee_base_link”. Read from the file’s own description of its end-effector frame. Empty when the file names none, lacks the end-effector poses, or they disagree with its controller_to_tcp (then no ee_to_tcp is set either). Display only.

ee_to_tcp is drawn in this link’s own frame, which must be the plain end-effector frame the file means. An export whose link sits elsewhere draws the TCP off by the difference: an OpenArm exported with the xacro default collapse_internal_empty_links puts openarm__ee_base_link at link7, 20.5 mm behind the frame the calibration is in.

string
mergedSettingsContext

Explicitly confirmed correction of one eligible LeRobot merged member. Its training adapter must consume observation.tcp and its own recognized rig supplies calibration; one rig’s calibration must not be fanned out to heterogeneous uploads. Omitted retains the old merged-away restriction.

object
mergedDataset
required

The merged dataset whose membership was confirmed.

string
members
required

Every upload it held, in merge order, including the selected upload.

Array<string>
membershipRevision
required

Opaque merge/undo epoch from GetDataset; never recapture it on retry.

string
etag
required

The dataset’s editable etag; a member edit sends the selected upload’s.

string
sourceCommit

The selected upload’s exact paired head. Empty on the merged dataset’s context, which is used only to fence global label edits.

string
requestId

UUID retry key, required with merged_settings_context.

string
validateOnly

Merged datasets only: run the same per-upload admission checks without creating a lease or dispatching ingest. All blockers are returned by name; a later move rechecks them. Standalone uploads retain their existing API.

boolean
members

Merged only: retry these held uploads. Empty selects every current member.

Array<string>
membershipRevision

Required for a merged move (not validate_only), from GetDataset’s pose_preparation. No caller calibration is accepted for merged datasets: the server reads each upload’s own recognized rig’s current calibration.

string
expectedOperationId

Resume only this existing pose operation; never admit a new move.

string

OK

Media type application/json

Response for Datasets.ResolvePoses.

object
operationId

The re-derive’s operation id, to poll through GET /datasets/sync-status/{operation_id}. Empty when no_op is set.

string
noOp

True when nothing needed doing (the upload already stands as asked), so there is nothing to wait for.

boolean
mergedPreparation

Merged dataset only: one result per upload. Eligible uploads may proceed despite other blockers. Retry joins the same running work; no batch job or implicit pose move on a robot selection is created.

object
membershipRevision

Opaque ordered-membership epoch. Send it back on an explicit move or removal; a changed membership is ABORTED and must be confirmed again.

string
currentMembers

Number already at their current capture calibration.

integer format: int32
totalMembers

Number of uploads in the current membership.

integer format: int32
members

Preparation state for each upload, in membership order.

Array<object>

One upload’s current state. Names and reasons are suitable for the merged dataset’s pre-check; no source data or historical rulings are removed.

object
upload

The source upload resource.

string
displayName

The source upload’s display name.

string
state

The upload’s current preparation state.

string format: enum
Allowed values: STATE_UNSPECIFIED CURRENT READY RUNNING BLOCKED FAILED
reason

Human-readable explanation when applicable.

string
operationId

UUID polled at this upload’s reingests/{operation_id} resource.

string
rigKey

Recording rig, never an editable collection tag or playback robot.

string
calibrationGripper

The capture gripper supplying the calibration.

string
calibrationEtag

The capture calibration’s version.

string
publishing

Preparation is complete and its publication is pending; it cannot be cancelled.

boolean
applicable

False when none of these recordings has handheld poses to prepare.

boolean
Example
{
"mergedPreparation": {
"members": [
{
"state": "STATE_UNSPECIFIED"
}
]
}
}

Default error response

Media type application/json

The Status type defines a logical error model that is suitable for different programming environments, including REST APIs and RPC APIs. It is used by gRPC. Each Status message contains three pieces of data: error code, error message, and error details. You can find out more about this error model and how to work with it in the API Design Guide.

object
code

The status code, which should be an enum value of [google.rpc.Code][google.rpc.Code].

integer format: int32
message

A developer-facing error message, which should be in English. Any user-facing error message should be localized and sent in the [google.rpc.Status.details][google.rpc.Status.details] field, or localized by the client.

string
details

A list of messages that carry the error details. There is a common set of message types for APIs to use.

Array<object>

Contains an arbitrary serialized message along with a @type that describes the type of the serialized message.

object
@type

The type of the serialized message.

string
key
additional properties
any
Example generated
{
"code": 1,
"message": "example",
"details": [
{
"@type": "example"
}
]
}