# Robots_GetRobotModel

`GET /v1/accounts/{account}/robots/{robot}/model`

Gets a robot's model. NOT_FOUND only when the robot has no imported model
and no import was ever started (fleet serves imported models only).
Otherwise returns the model, possibly with no URDF yet, with its
import_state, so a first import can be polled and a FAILED one is seen.

- Base URL: `https://junction-api.qualiastudios.dev`
- Authentication: an API key in the `X-API-Key` header

## Parameters

- `account` (path, string, required): The account id.
- `robot` (path, string, required): The robot id.

## Responses

- `200` OK: `application/json` `qualia.v1.RobotModel`
- `default` Default error response: `application/json` `google.rpc.Status`

## Schemas

```yaml
qualia.v1.RobotModel:
  type: object
  properties:
    name:
      type: string
      description: accounts/{account}/robots/{robot}/model
    urdfRobot:
      readOnly: true
      type: string
      description: The URDF's own robot name.
    urdfUri:
      readOnly: true
      type: string
      description: "Signed URI of the browser-safe URDF: meshes point at the GLBs below."
    meshes:
      readOnly: true
      type: array
      items:
        $ref: "#/components/schemas/qualia.v1.RobotModel_Mesh"
      description: The meshes.
    digest:
      readOnly: true
      type: string
      description: Content digest of the imported URDF and meshes.
    arms:
      type: array
      items:
        $ref: "#/components/schemas/qualia.v1.RobotModel_Arm"
      description: |-
        Its arms: guessed at import (the deepest link named for each side, else
        one right arm), then edited.
    solver:
      enum:
        - SOLVER_UNSPECIFIED
        - PYROKI
        - DLS
        - MINK
      type: string
      description: |-
        The IK solver; SOLVER_UNSPECIFIED when none has been set (a new model has
        none). Once set it can be changed to another, never cleared: naming
        `solver` in the update mask with SOLVER_UNSPECIFIED, or with a value this
        server does not know, is INVALID_ARGUMENT.
      format: enum
    gripper:
      allOf:
        - $ref: "#/components/schemas/qualia.v1.RobotModel_Gripper"
      description: The gripper joint; unset when the robot has none.
    capsuleState:
      readOnly: true
      enum:
        - CAPSULE_STATE_UNSPECIFIED
        - FITTING
        - FITTED
        - UNAVAILABLE
      type: string
      description: Self-collision capsules.
      format: enum
    capsulesUri:
      readOnly: true
      type: string
      description: Signed URI of the capsules JSON (pyroki's export), when fitted.
    configDigest:
      readOnly: true
      type: string
      description: |-
        Digest over digest, arms, solver, capsules, gripper and the solver's
        parameters (mink_parameters for MINK, pyroki_parameters for PYROKI, the
        legacy numbers excepted). Reach results
        are keyed by it: after a change the stored results are for the old
        config, and nothing re-runs them until a reach check is asked for.
    editor:
      readOnly: true
      allOf:
        - $ref: "#/components/schemas/qualia.v1.Actor"
      description: Who last changed it.
    updateTime:
      readOnly: true
      type: string
      description: When it last changed.
      format: date-time
    etag:
      type: string
      description: Sent back on UpdateRobotModel.
    importState:
      readOnly: true
      enum:
        - IMPORT_STATE_UNSPECIFIED
        - PENDING
        - CONVERTING
        - READY
        - FAILED
      type: string
      description: |-
        Where the latest import is. Until it is READY, the fields above describe
        the previous model (empty when there is none).
      format: enum
    importFailureReason:
      readOnly: true
      type: string
      description: |-
        Why the latest import FAILED, e.g. "no link is reached through three
        turning joints".
    reachBase:
      type: array
      items:
        type: number
        format: double
      description: |-
        Where a dataset's reach check starts when the dataset names no base of
        its own (ReachSettings.base_m): empty or exactly three numbers, metres in
        the robot's base frame, each within 5 m either way. Not part of
        config_digest, so changing it makes no stored result stale; a dataset that
        follows it gets a new reach key on its next RunCheck. The reach op does
        not apply a base yet; see ReachSettings.base_m.
    tcpDigest:
      readOnly: true
      type: string
      description: |-
        Digest of every arm's TCP (Arm.tcp), empty when no arm has one. Reach
        results walked at the TCP are keyed by it beside config_digest, so a
        change to the carried gripper, its mount, its TCP or either URDF makes
        them stale; results walked at the gripper root do not depend on it.
    minkParameters:
      allOf:
        - $ref: "#/components/schemas/qualia.v1.RobotModel_MinkParameters"
      description: |-
        What Mink solves with: set exactly when the solver is MINK, and then whole
        (see MinkParameters). Set with UpdateRobotModel (update_mask
        `mink_parameters`) together with solver MINK; any other solver clears
        them. Part of config_digest, so changing them makes stored reach results
        stale.
    pyrokiParameters:
      allOf:
        - $ref: "#/components/schemas/qualia.v1.RobotModel_PyrokiParameters"
      description: |-
        What PyRoKi solves with: set exactly when the solver is PYROKI, and then
        whole (see PyrokiParameters). Set with UpdateRobotModel (update_mask
        `pyroki_parameters`) together with solver PYROKI, as `mink_parameters` is
        with MINK; any other solver clears them. A PYROKI robot that has run before
        holds the numbers it has always run. Part of config_digest, except that the
        numbers every PyRoKi robot ran before it had a file add nothing, so a robot
        that still holds them keeps the digest (and the stored reach results) it
        had.
  description: |-
    A robot's kinematic model and the config every consumer shares: reach
    checks, retargeting, forward kinematics for trajectories, and the 3D
    gripper. One per robot.

    Served by fleet.
qualia.v1.RobotModel_Mesh:
  type: object
  properties:
    path:
      readOnly: true
      type: string
      description: The path in the served URDF (package:// resolved to this relative path).
    uri:
      readOnly: true
      type: string
      description: Signed URI of the GLB.
  description: One mesh, converted to GLB, at the path the served URDF references.
qualia.v1.RobotModel_Arm:
  required:
    - side
    - handLink
  type: object
  properties:
    side:
      enum:
        - SIDE_UNSPECIFIED
        - SIDE_LEFT
        - SIDE_RIGHT
      type: string
      description: Which side.
      format: enum
    handLink:
      type: string
      description: The link the arm ends at.
    tool:
      allOf:
        - $ref: "#/components/schemas/qualia.v1.Transform"
      description: The tool frame fixed in the hand link, where a handheld gripper root is held.
    startPosture:
      type: array
      items:
        type: number
        format: double
      description: |-
        The posture a replay starts from: one angle per turning joint of the
        chain, radians, clamped to the limits.
    joints:
      readOnly: true
      type: array
      items:
        $ref: "#/components/schemas/qualia.v1.RobotModel_Joint"
      description: The chain's turning joints, base to hand, with their limits.
    tcp:
      readOnly: true
      allOf:
        - $ref: "#/components/schemas/qualia.v1.Matrix4"
      description: |-
        The arm's TCP in the hand link's frame, the one every reader of a robot's
        TCP uses (the reach solve at the TCP, the dashboard). A robot carrying a
        gripper (Robot.carried_gripper): the hand link to the mount's parent
        link, then the mount, then the gripper's own TCP; empty when the gripper
        has no TCP for this side or it is stale, or the mount's parent link is
        not joined to the hand link by fixed joints. A robot carrying none: the
        TCP its URDF names, the first link called `grasp_frame`, `hand_tcp` or
        `tcp` (or ending `_` and one of those) fixed below the hand link; empty
        where it names none.
    startPostureConfigured:
      type: boolean
      description: |-
        Explicitly use this complete, in-limit start_posture as configured
        home for contact exclusions. Absent/false retains the replay rest seed
        without claiming imported, padded or clipped defaults are verified home.
  description: One arm.
qualia.v1.Transform:
  type: object
  properties:
    xyz:
      type: array
      items:
        type: number
        format: double
      description: x, y, z in metres.
    rpy:
      type: array
      items:
        type: number
        format: double
      description: roll, pitch, yaw in radians.
  description: |-
    A 3D rigid transform: translation in metres and roll, pitch, yaw in radians
    (URDF convention, extrinsic XYZ).
qualia.v1.RobotModel_Joint:
  type: object
  properties:
    joint:
      readOnly: true
      type: string
      description: Joint name.
    lower:
      readOnly: true
      type: number
      description: Lower limit, radians (or metres for prismatic).
      format: double
    upper:
      readOnly: true
      type: number
      description: Upper limit.
      format: double
  description: One turning joint, read off the URDF.
qualia.v1.Matrix4:
  type: object
  properties:
    m:
      type: array
      items:
        type: number
        format: double
      description: m[0..3] is the first row, m[12..15] the last.
  description: |-
    A 3D rigid transform as a 4x4 matrix: 16 numbers, row-major, translation in
    metres in the last column, the bottom row 0 0 0 1. Where it is used says
    whose pose it is and in which frame (Robot.GripperTcp.Hand.tcp: the TCP in
    a link's frame). Unlike Transform it has no pitch pole to lose a rotation at.
qualia.v1.RobotModel_Gripper:
  required:
    - joint
    - openPosition
    - closedPosition
  type: object
  properties:
    joint:
      type: string
      description: Joint name; `{side}` is replaced by "left" or "right".
    openPosition:
      type: number
      description: Its position fully open.
      format: double
    closedPosition:
      type: number
      description: Its position shut.
      format: double
    homePosition:
      type: number
      description: |-
        Explicit jaw position at the configured arm home, mirrored with the
        open/closed range where the side's URDF limits require it. Absence means
        unknown, including on legacy profiles; never infer open, closed or zero.
      format: double
  description: |-
    The joint that opens the gripper. A robot that carries a gripper
    (Robot.carried_gripper) has that gripper's own jaw here, none when the gripper
    has none, not the stock fingers its URDF came on; taking the gripper off gives
    the robot's own jaw back. The gripper's own model holds the same jaw.
qualia.v1.Actor:
  type: object
  properties:
    kind:
      readOnly: true
      enum:
        - KIND_UNSPECIFIED
        - PERSON
        - VALIDATION
        - RULE
        - MODEL
        - OPERATOR
        - SYSTEM
      type: string
      description: What kind of actor it is.
      format: enum
    user:
      readOnly: true
      type: string
      description: The person's user id when `kind` is PERSON; empty otherwise.
    displayName:
      readOnly: true
      type: string
      description: The person's display name at the time, for rendering without a lookup.
    agent:
      readOnly: true
      type: string
      description: |-
        The rule code, or for a MODEL the Qualia model tier (e.g. "qualia-vlm";
        "qualia" for any other model), when not a person.
    run:
      readOnly: true
      type: string
      description: The run a model or rule finding came from, when it has one.
    apiKey:
      readOnly: true
      type: string
      description: |-
        The API key's id when a person acted through one; empty for a session.
        Never the key itself.
    attribution:
      readOnly: true
      allOf:
        - $ref: "#/components/schemas/qualia.v1.Attribution"
      description: |-
        Signed identity evidence recorded with the change, kept apart from the
        principal above. Unset on older records and wherever none was signed.
  description: Who did something. Every decision, issue and reply names one.
qualia.v1.Attribution:
  type: object
  properties:
    authentication:
      readOnly: true
      enum:
        - AUTHENTICATION_UNSPECIFIED
        - SESSION
        - API_KEY
        - SERVICE
      type: string
      description: How the principal authenticated.
      format: enum
    initiator:
      readOnly: true
      type: string
      description: |-
        The user id of a person verified as starting the work: the person of a
        session, or the person a trusted service's signed job record names.
        Empty when nobody was verified.
    initiatorDisplayName:
      readOnly: true
      type: string
      description: The initiator's display name at the time, never their email.
    credentialCreator:
      readOnly: true
      type: string
      description: The user id of the key's creator at the time. Not who used the key.
    credentialOwner:
      readOnly: true
      enum:
        - CREDENTIAL_OWNER_UNSPECIFIED
        - PERSON
        - SPACE
      type: string
      description: Who owned the key at the time.
      format: enum
    credentialOwnerSpace:
      readOnly: true
      type: string
      description: |-
        The owning space's id (a workspace or the organization) when
        `credential_owner` is SPACE.
  description: |-
    How a change was authenticated and who verifiably started it.
    Only the trusted mesh signs it. It never authorizes anything, and a key's
    creator or owner is never the person who used the key.
qualia.v1.RobotModel_MinkParameters:
  type: object
  properties:
    posCost:
      type: number
      description: |-
        Each arm's frame task on its tool frame: its position cost, 0 or more.
        pos_cost and ori_cost are not both 0.
      format: double
    oriCost:
      type: number
      description: The frame task's orientation cost, 0 or more.
      format: double
    frameLmDamping:
      type: number
      description: The frame task's Levenberg-Marquardt damping, 0 or more.
      format: double
    postureCost:
      type: number
      description: The posture task's cost on the arm joints, 0 or more.
      format: double
    postureTarget:
      enum:
        - POSTURE_TARGET_UNSPECIFIED
        - ENGAGE
        - ROW
      type: string
      description: What the posture task pulls the arm joints toward.
      format: enum
    smoothnessCost:
      type: number
      description: The smoothness task's cost, 0 or more; 0 turns it off.
      format: double
    elbowDownCost:
      type: number
      description: The elbow-down task's cost, 0 or more; 0 turns it off.
      format: double
    singularityThreshold:
      type: number
      description: The singularity task's threshold, 0 or more; 0 turns it off.
      format: double
    singularityDamping:
      type: number
      description: The singularity task's damping, 0 or more; 0 turns it off.
      format: double
    dt:
      type: number
      description: The QP's step, above 0.
      format: double
    damping:
      type: number
      description: The QP's Levenberg-Marquardt damping, 0 or more.
      format: double
    maxIters:
      type: integer
      description: The most QP steps a row takes, 1 to 1000.
      format: int32
    posTol:
      type: number
      description: |-
        A row's steps stop once every arm's position error is within this and
        its orientation error within ori_tol; a 0 tolerance is never met.
      format: double
    oriTol:
      type: number
      description: The orientation tolerance that goes with pos_tol; 0 is never met.
      format: double
    stallTol:
      type: number
      description: |-
        A row's steps stop once a step moves the joints less than this; 0
        never stops one.
      format: double
    progressWindow:
      type: integer
      description: |-
        A row's steps stop once its error shrank by less than min_progress over
        this many steps, 0 to 1000; 0 never stops one.
      format: int32
    minProgress:
      type: number
      description: |-
        The share of its error a row must lose over progress_window steps to
        keep stepping, 0 or more and below 1.
      format: double
    checkedSteps:
      type: boolean
      description: q-research's checked step (inference) instead of mink.solve_ik (teleop).
    maxJointVelocity:
      type: number
      description: The arm joints' speed limit in the QP, 0 or more; 0 is no limit.
      format: double
    maxJointSpeedPerRow:
      type: number
      description: |-
        The arm joints' speed limit from one row of the take to the next, 0 or
        more; 0 is no limit.
      format: double
    selfCollision:
      type: boolean
      description: Whether the collision limit, on the robot's capsules, is on.
    collisionPairs:
      enum:
        - COLLISION_PAIRS_UNSPECIFIED
        - ALL
        - AVOIDED
      type: string
      description: The capsule pairs the collision limit watches.
      format: enum
    collisionMinDistance:
      type: number
      description: The collision limit's minimum distance, 0 or more.
      format: double
    collisionDetectionDistance:
      type: number
      description: The collision limit's detection distance, above collision_min_distance.
      format: double
    collisionGain:
      type: number
      description: The collision limit's gain, above 0 and at most 1.
      format: double
    collisionBoundRelaxation:
      type: number
      description: The collision limit's bound relaxation, 0 or more.
      format: double
    shoulderLink:
      type: string
      description: |-
        The shoulder link's name ("{side}" stands for left or right, at most 256
        characters); empty is the arm chain's 2nd joint's child link.
    elbowLink:
      type: string
      description: |-
        The elbow link's name, like shoulder_link; empty is the 4th joint's
        child link.
    wristLink:
      type: string
      description: |-
        The wrist link's name, like shoulder_link; empty is the 5th joint's
        child link.
  description: |-
    What Mink solves with (Solver MINK): schema 1 of the reach op's contract
    (op_reach.contract.MinkParameters). Every field is required: there are no
    defaults and no built-in sets. Metres, radians and seconds.
qualia.v1.RobotModel_PyrokiParameters:
  type: object
  properties:
    posWeight:
      type: number
      description: |-
        The pose cost's weight on each hand's position, 0 to 1e6. pos_weight and
        ori_weight are not both 0.
      format: double
    oriWeight:
      type: number
      description: The pose cost's weight on each hand's orientation, 0 to 1e6.
      format: double
    lastWeight:
      type: number
      description: |-
        The weight pulling each row toward the last answer, 0 to 1e6. It pulls
        toward the same value as anchor_weight, so only their sum of squares
        matters and tuning either has the same effect.
      format: double
    anchorWeight:
      type: number
      description: |-
        The weight pulling each row toward the row before it, 0 to 1e6. Not toward
        home: see last_weight.
      format: double
    manipulabilityWeight:
      type: number
      description: The weight on the arm's manipulability, 0 to 1e6.
      format: double
    limitWeight:
      type: number
      description: The weight keeping the joints inside their limits, 0 to 1e6.
      format: double
    collisionWeight:
      type: number
      description: The weight keeping the arms' capsules apart, 0 to 1e6.
      format: double
    collisionMargin:
      type: number
      description: |-
        How near two capsules may come before the collision cost acts, in metres,
        0 to 1.
      format: double
    maxIterations:
      type: integer
      description: The most solver iterations a row takes, 1 to 50.
      format: int32
  description: |-
    What PyRoKi solves with (Solver PYROKI): schema 1 of the PyRoKi solver
    file, as fleet checks it (q_fleet_api.services.pyroki_parameters). qds sends
    it to the reach op with each solve, as it sends a Mink robot's parameters. Every field is required: there are no defaults and no built-in
    sets. Weights are unitless, the margin is metres.
    Each robot has its own: PyRoKi on one robot and PyRoKi on another run the same
    solver and are tuned apart.
google.rpc.Status:
  oneOf:
    - $ref: "#/components/schemas/ApiError"
    - $ref: "#/components/schemas/GatewayError"
  description: "The body of a failed call: one of the two shapes below."
ApiError:
  title: API error
  type: object
  required:
    - code
    - message
    - retryable
    - details
  properties:
    code:
      type: string
      description: The gRPC code name in upper case, e.g. `NOT_FOUND`.
    message:
      type: string
    retryable:
      type: boolean
      description: Whether the same call may succeed if retried.
    details:
      type: array
      items:
        type: object
      description: "Empty, except when a dataset copy or move is refused: then one `google.rpc.ErrorInfo` with the refusal's `reason` code, its `domain` and, in `metadata`, the datasets it names."
  description: A refusal from the API itself.
GatewayError:
  title: Gateway error
  type: object
  required:
    - detail
  properties:
    detail:
      type: string
      description: What went wrong.
  description: A refusal from the gateway before the call reaches the API, for example `401` bad or missing API key, `403` key lacks the scope or environment closed, `413` body too large.
```
