{
  "openapi": "3.1.0",
  "info": {
    "title": "RSX Link",
    "version": "v1",
    "description": "Device identity for RSX Link: enrolling an installation with your RSX ID approval and a key it holds, listing your devices, and revoking them."
  },
  "servers": [
    {
      "url": "https://api.rsx.group"
    }
  ],
  "components": {
    "schemas": {
      "LinkEnrolment": {
        "type": "object",
        "properties": {
          "id": {
            "type": "string",
            "format": "uuid",
            "example": "0199d6a1-3b2c-7d4e-8f50-6a7b8c9d0e1f"
          },
          "state": {
            "type": "string",
            "enum": [
              "pending",
              "approved",
              "denied",
              "consumed",
              "expired",
              "cancelled"
            ],
            "example": "pending"
          },
          "userCode": {
            "type": "string",
            "pattern": "^[A-Z]{4}-[A-Z]{4}$",
            "example": "BCDF-GHJK"
          },
          "approvalUri": {
            "type": "string",
            "format": "uri",
            "example": "https://id.rsx.group/link"
          },
          "approvalUriComplete": {
            "type": "string",
            "format": "uri",
            "example": "https://id.rsx.group/link?code=BCDF-GHJK"
          },
          "keyFingerprint": {
            "type": "string",
            "minLength": 43,
            "maxLength": 43,
            "pattern": "^[A-Za-z0-9_-]+$",
            "description": "RFC 7638 JWK thumbprint (SHA-256) of the device public key, base64url.",
            "example": "NzbLsXh8uDCcd-6MNwXF4W_7noWXFZAfHkxZsRGC9Xs"
          },
          "createdAt": {
            "type": "string",
            "format": "date-time",
            "example": "2026-10-13T12:00:00.000Z"
          },
          "expiresAt": {
            "type": "string",
            "format": "date-time",
            "example": "2026-10-13T12:00:00.000Z"
          },
          "pollIntervalSeconds": {
            "type": "integer",
            "example": 5
          },
          "challenge": {
            "type": [
              "string",
              "null"
            ],
            "minLength": 43,
            "maxLength": 43,
            "pattern": "^[A-Za-z0-9_-]+$",
            "description": "Set only while `approved`: sign it into the enrolment proof and call devices/add.",
            "example": null
          },
          "deviceId": {
            "type": [
              "string",
              "null"
            ],
            "format": "uuid",
            "description": "Set once `consumed`.",
            "example": null
          }
        },
        "required": [
          "id",
          "state",
          "userCode",
          "approvalUri",
          "approvalUriComplete",
          "keyFingerprint",
          "createdAt",
          "expiresAt",
          "pollIntervalSeconds",
          "challenge",
          "deviceId"
        ]
      },
      "LinkEnrolmentCreated": {
        "type": "object",
        "properties": {
          "enrolment": {
            "$ref": "#/components/schemas/LinkEnrolment"
          },
          "secret": {
            "type": "string",
            "description": "The enrolment secret, shown once. Send it as `Authorization: Bearer` to enrolments/get, enrolments/remove and devices/add.",
            "example": "rsx_lke_Zm9vYmFyYmF6cXV4cXV1eGNvcmdlZ3JhdWx0Z2FycGx5"
          }
        },
        "required": [
          "enrolment",
          "secret"
        ]
      },
      "Problem": {
        "type": "object",
        "properties": {
          "type": {
            "type": "string",
            "example": "https://docs.rsx.group/errors/not-found"
          },
          "title": {
            "type": "string",
            "example": "Not found"
          },
          "status": {
            "type": "integer",
            "example": 404
          },
          "detail": {
            "type": "string"
          },
          "instance": {
            "type": "string",
            "example": "/access/v1/installations/get"
          },
          "requestId": {
            "type": "string"
          }
        },
        "required": [
          "type",
          "title",
          "status"
        ]
      },
      "LinkCreateEnrolment": {
        "type": "object",
        "properties": {
          "protocol": {
            "type": "integer",
            "description": "Must be 1.",
            "example": 1
          },
          "keyAlgorithm": {
            "type": "string",
            "enum": [
              "ES256"
            ],
            "example": "ES256"
          },
          "publicKey": {
            "type": "string",
            "minLength": 80,
            "maxLength": 200,
            "pattern": "^[A-Za-z0-9_-]+$",
            "description": "The device's P-256 public key, SPKI DER encoded, base64url without padding.",
            "example": "MFkwEwYHKoZIzj0CAQYIKoZIzj0DAQcDQgAEyVDx2fAX3rA6m0EIUzBBWkA4zG6-n9t9Sb8c2X5wXn0q2xvGQaU2C9Uw8Yw4rX4n2m4R3x8Gm8fGJf9k5Yy3sA"
          },
          "platform": {
            "type": "string",
            "maxLength": 32,
            "description": "One of: windows, linux, android.",
            "example": "windows"
          },
          "displayName": {
            "type": "string",
            "minLength": 1,
            "maxLength": 64,
            "example": "Ada's desktop"
          },
          "platformVersion": {
            "type": "string",
            "minLength": 1,
            "maxLength": 64,
            "example": "Windows 11 Pro 24H2"
          },
          "agentVersion": {
            "type": "string",
            "minLength": 1,
            "maxLength": 32,
            "example": "0.2.0"
          }
        },
        "required": [
          "protocol",
          "keyAlgorithm",
          "publicKey",
          "platform"
        ],
        "additionalProperties": false
      },
      "LinkRemoveEnrolment": {
        "type": "object",
        "properties": {
          "id": {
            "type": "string",
            "format": "uuid",
            "example": "0199d6a1-3b2c-7d4e-8f50-6a7b8c9d0e1f"
          }
        },
        "required": [
          "id"
        ],
        "additionalProperties": false
      },
      "LinkDevice": {
        "type": "object",
        "properties": {
          "id": {
            "type": "string",
            "format": "uuid",
            "example": "0199d6a1-3b2c-7d4e-8f50-6a7b8c9d0e1f"
          },
          "displayName": {
            "type": "string",
            "minLength": 1,
            "maxLength": 64,
            "example": "Ada's desktop"
          },
          "platform": {
            "type": "string",
            "enum": [
              "windows",
              "linux",
              "android"
            ],
            "example": "windows"
          },
          "platformVersion": {
            "type": [
              "string",
              "null"
            ],
            "example": "Windows 11 Pro 24H2"
          },
          "agentVersion": {
            "type": [
              "string",
              "null"
            ],
            "example": "0.2.0"
          },
          "keyAlgorithm": {
            "type": "string",
            "enum": [
              "ES256"
            ]
          },
          "keyFingerprint": {
            "type": "string",
            "minLength": 43,
            "maxLength": 43,
            "pattern": "^[A-Za-z0-9_-]+$",
            "description": "RFC 7638 JWK thumbprint (SHA-256) of the device public key, base64url.",
            "example": "NzbLsXh8uDCcd-6MNwXF4W_7noWXFZAfHkxZsRGC9Xs"
          },
          "status": {
            "type": "string",
            "enum": [
              "active",
              "revoked"
            ],
            "example": "active"
          },
          "createdAt": {
            "type": "string",
            "format": "date-time",
            "example": "2026-10-13T12:00:00.000Z"
          },
          "lastAuthorisedAt": {
            "type": [
              "string",
              "null"
            ],
            "format": "date-time",
            "example": null
          },
          "revokedAt": {
            "type": [
              "string",
              "null"
            ],
            "format": "date-time",
            "example": null
          },
          "revokedReason": {
            "type": [
              "string",
              "null"
            ],
            "enum": [
              "owner"
            ],
            "example": null
          }
        },
        "required": [
          "id",
          "displayName",
          "platform",
          "platformVersion",
          "agentVersion",
          "keyAlgorithm",
          "keyFingerprint",
          "status",
          "createdAt",
          "lastAuthorisedAt",
          "revokedAt",
          "revokedReason"
        ]
      },
      "LinkDeviceCreated": {
        "type": "object",
        "properties": {
          "device": {
            "$ref": "#/components/schemas/LinkDevice"
          }
        },
        "required": [
          "device"
        ]
      },
      "LinkCreateDevice": {
        "type": "object",
        "properties": {
          "enrolmentId": {
            "type": "string",
            "format": "uuid",
            "example": "0199d6a1-3b2c-7d4e-8f50-6a7b8c9d0e1f"
          },
          "keyFingerprint": {
            "type": "string",
            "minLength": 43,
            "maxLength": 43,
            "pattern": "^[A-Za-z0-9_-]+$",
            "description": "RFC 7638 JWK thumbprint (SHA-256) of the device public key, base64url.",
            "example": "NzbLsXh8uDCcd-6MNwXF4W_7noWXFZAfHkxZsRGC9Xs"
          },
          "signature": {
            "type": "string",
            "minLength": 1,
            "maxLength": 200,
            "pattern": "^[A-Za-z0-9_-]+$",
            "description": "ECDSA P-256 SHA-256 signature as raw r||s (64 bytes), base64url without padding.",
            "example": "q1Vf3n0dJpZx0Vb6q2HcX9yXjXcVv8bN1rQ8eYc5wD4w6s3VtJxk2nB0eQ7pY5uR9sT3vW1xZ2aB4cD6eF8gH0"
          }
        },
        "required": [
          "enrolmentId",
          "keyFingerprint",
          "signature"
        ],
        "additionalProperties": false
      },
      "LinkChallenge": {
        "type": "object",
        "properties": {
          "id": {
            "type": "string",
            "format": "uuid",
            "example": "0199d6a1-3b2c-7d4e-8f50-6a7b8c9d0e1f"
          },
          "challenge": {
            "type": "string",
            "minLength": 43,
            "maxLength": 43,
            "pattern": "^[A-Za-z0-9_-]+$",
            "example": "3q2-7wAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA"
          },
          "expiresAt": {
            "type": "string",
            "format": "date-time",
            "example": "2026-10-13T12:00:00.000Z"
          }
        },
        "required": [
          "id",
          "challenge",
          "expiresAt"
        ]
      },
      "LinkCreateChallenge": {
        "type": "object",
        "properties": {
          "deviceId": {
            "type": "string",
            "format": "uuid",
            "example": "0199d6a1-3b2c-7d4e-8f50-6a7b8c9d0e1f"
          }
        },
        "required": [
          "deviceId"
        ],
        "additionalProperties": false
      },
      "LinkSessionCreated": {
        "type": "object",
        "properties": {
          "token": {
            "type": "string",
            "description": "The device session, shown once. Send it as `Authorization: Bearer` on device-authorised calls.",
            "example": "rsx_lks_c2Vzc2lvbnRva2VudmFsdWVub3RyZWFsYXRhbGxvaw"
          },
          "tokenType": {
            "type": "string",
            "enum": [
              "Bearer"
            ]
          },
          "expiresAt": {
            "type": "string",
            "format": "date-time",
            "example": "2026-10-13T12:00:00.000Z"
          },
          "expiresIn": {
            "type": "integer",
            "example": 900
          },
          "device": {
            "$ref": "#/components/schemas/LinkDevice"
          }
        },
        "required": [
          "token",
          "tokenType",
          "expiresAt",
          "expiresIn",
          "device"
        ]
      },
      "LinkCreateSession": {
        "type": "object",
        "properties": {
          "protocol": {
            "type": "integer",
            "example": 1
          },
          "deviceId": {
            "type": "string",
            "format": "uuid",
            "example": "0199d6a1-3b2c-7d4e-8f50-6a7b8c9d0e1f"
          },
          "challengeId": {
            "type": "string",
            "format": "uuid",
            "example": "0199d6a1-3b2c-7d4e-8f50-6a7b8c9d0e1f"
          },
          "signature": {
            "type": "string",
            "minLength": 1,
            "maxLength": 200,
            "pattern": "^[A-Za-z0-9_-]+$",
            "description": "ECDSA P-256 SHA-256 signature as raw r||s (64 bytes), base64url without padding.",
            "example": "q1Vf3n0dJpZx0Vb6q2HcX9yXjXcVv8bN1rQ8eYc5wD4w6s3VtJxk2nB0eQ7pY5uR9sT3vW1xZ2aB4cD6eF8gH0"
          }
        },
        "required": [
          "protocol",
          "deviceId",
          "challengeId",
          "signature"
        ],
        "additionalProperties": false
      },
      "LinkSession": {
        "type": "object",
        "properties": {
          "deviceId": {
            "type": "string",
            "format": "uuid",
            "example": "0199d6a1-3b2c-7d4e-8f50-6a7b8c9d0e1f"
          },
          "expiresAt": {
            "type": "string",
            "format": "date-time",
            "example": "2026-10-13T12:00:00.000Z"
          },
          "device": {
            "$ref": "#/components/schemas/LinkDevice"
          }
        },
        "required": [
          "deviceId",
          "expiresAt",
          "device"
        ]
      },
      "LinkApproval": {
        "type": "object",
        "properties": {
          "enrolmentId": {
            "type": "string",
            "format": "uuid",
            "example": "0199d6a1-3b2c-7d4e-8f50-6a7b8c9d0e1f"
          },
          "userCode": {
            "type": "string",
            "pattern": "^[A-Z]{4}-[A-Z]{4}$",
            "example": "BCDF-GHJK"
          },
          "state": {
            "type": "string",
            "enum": [
              "pending",
              "approved",
              "denied"
            ],
            "example": "pending"
          },
          "createdAt": {
            "type": "string",
            "format": "date-time",
            "example": "2026-10-13T12:00:00.000Z"
          },
          "expiresAt": {
            "type": "string",
            "format": "date-time",
            "example": "2026-10-13T12:00:00.000Z"
          },
          "keyFingerprint": {
            "type": "string",
            "minLength": 43,
            "maxLength": 43,
            "pattern": "^[A-Za-z0-9_-]+$",
            "description": "RFC 7638 JWK thumbprint (SHA-256) of the device public key, base64url.",
            "example": "NzbLsXh8uDCcd-6MNwXF4W_7noWXFZAfHkxZsRGC9Xs"
          },
          "requestCountry": {
            "type": [
              "string",
              "null"
            ],
            "description": "Where the enrolment was opened from, by the edge's estimate.",
            "example": "GB"
          },
          "device": {
            "type": "object",
            "properties": {
              "displayName": {
                "type": "string",
                "example": "Ada's desktop"
              },
              "platform": {
                "type": "string",
                "enum": [
                  "windows",
                  "linux",
                  "android"
                ]
              },
              "platformVersion": {
                "type": [
                  "string",
                  "null"
                ]
              },
              "agentVersion": {
                "type": [
                  "string",
                  "null"
                ]
              }
            },
            "required": [
              "displayName",
              "platform",
              "platformVersion",
              "agentVersion"
            ]
          }
        },
        "required": [
          "enrolmentId",
          "userCode",
          "state",
          "createdAt",
          "expiresAt",
          "keyFingerprint",
          "requestCountry",
          "device"
        ]
      },
      "LinkUpdateApproval": {
        "type": "object",
        "properties": {
          "enrolmentId": {
            "type": "string",
            "format": "uuid",
            "example": "0199d6a1-3b2c-7d4e-8f50-6a7b8c9d0e1f"
          },
          "code": {
            "type": "string",
            "minLength": 8,
            "maxLength": 16,
            "description": "The code shown by the device. Case and hyphens are ignored.",
            "example": "BCDF-GHJK"
          },
          "decision": {
            "type": "string",
            "enum": [
              "approve",
              "deny"
            ]
          },
          "displayName": {
            "type": "string",
            "minLength": 1,
            "maxLength": 64,
            "description": "Name the device as it will appear in your list. Defaults to the name the device proposed.",
            "example": "Ada's desktop"
          }
        },
        "required": [
          "enrolmentId",
          "code",
          "decision"
        ],
        "additionalProperties": false
      },
      "LinkPageMeta": {
        "type": "object",
        "properties": {
          "cursor": {
            "type": [
              "string",
              "null"
            ],
            "example": null
          }
        },
        "required": [
          "cursor"
        ]
      },
      "LinkUpdateDevice": {
        "type": "object",
        "properties": {
          "id": {
            "type": "string",
            "format": "uuid",
            "example": "0199d6a1-3b2c-7d4e-8f50-6a7b8c9d0e1f"
          },
          "displayName": {
            "type": "string",
            "minLength": 1,
            "maxLength": 64,
            "example": "Ada's desktop"
          }
        },
        "required": [
          "id",
          "displayName"
        ],
        "additionalProperties": false
      },
      "LinkRemoveDevice": {
        "type": "object",
        "properties": {
          "id": {
            "type": "string",
            "format": "uuid",
            "example": "0199d6a1-3b2c-7d4e-8f50-6a7b8c9d0e1f"
          }
        },
        "required": [
          "id"
        ],
        "additionalProperties": false
      }
    },
    "parameters": {}
  },
  "paths": {
    "/link/v1/enrolments/add": {
      "post": {
        "operationId": "addEnrolment",
        "summary": "Open an enrolment for a device key",
        "description": "Called by a Link agent that is not yet a device. No credential: the gateway limits this route per address, and requires an `Idempotency-Key`; retrying with the same key and body after a lost response returns the original.\n\nReturns the enrolment, its `userCode` and `approvalUriComplete` for the person, and `secret`, shown once. The secret authenticates enrolments/get, enrolments/remove and devices/add for this enrolment only.",
        "tags": [
          "enrolments"
        ],
        "requestBody": {
          "content": {
            "application/json": {
              "schema": {
                "$ref": "#/components/schemas/LinkCreateEnrolment"
              }
            }
          }
        },
        "responses": {
          "200": {
            "description": "Enrolment opened",
            "content": {
              "application/json": {
                "schema": {
                  "type": "object",
                  "properties": {
                    "data": {
                      "$ref": "#/components/schemas/LinkEnrolmentCreated"
                    }
                  },
                  "required": [
                    "data"
                  ]
                }
              }
            }
          },
          "400": {
            "description": "Unsupported protocol or platform, an invalid public key, or a malformed request",
            "content": {
              "application/problem+json": {
                "schema": {
                  "$ref": "#/components/schemas/Problem"
                }
              }
            }
          },
          "409": {
            "description": "The key is already an active device, or belonged to a revoked one",
            "content": {
              "application/problem+json": {
                "schema": {
                  "$ref": "#/components/schemas/Problem"
                }
              }
            }
          }
        }
      }
    },
    "/link/v1/enrolments/get": {
      "get": {
        "operationId": "getEnrolment",
        "summary": "Poll an enrolment",
        "description": "Authenticated with the enrolment secret as `Authorization: Bearer rsx_lke_…`. Poll every `pollIntervalSeconds`. Every state is a 200; `challenge` is set only while `approved`, and `deviceId` once `consumed`.",
        "tags": [
          "enrolments"
        ],
        "parameters": [
          {
            "schema": {
              "type": "string",
              "format": "uuid",
              "example": "0199d6a1-3b2c-7d4e-8f50-6a7b8c9d0e1f"
            },
            "required": true,
            "name": "id",
            "in": "query"
          }
        ],
        "responses": {
          "200": {
            "description": "Enrolment",
            "content": {
              "application/json": {
                "schema": {
                  "type": "object",
                  "properties": {
                    "data": {
                      "$ref": "#/components/schemas/LinkEnrolment"
                    }
                  },
                  "required": [
                    "data"
                  ]
                }
              }
            }
          },
          "400": {
            "description": "Malformed request",
            "content": {
              "application/problem+json": {
                "schema": {
                  "$ref": "#/components/schemas/Problem"
                }
              }
            }
          },
          "404": {
            "description": "No such enrolment for this secret",
            "content": {
              "application/problem+json": {
                "schema": {
                  "$ref": "#/components/schemas/Problem"
                }
              }
            }
          }
        }
      }
    },
    "/link/v1/enrolments/remove": {
      "post": {
        "operationId": "removeEnrolment",
        "summary": "Cancel an enrolment",
        "description": "Authenticated with the enrolment secret. Cancels a pending or approved enrolment. Repeatable: an enrolment that has already ended is returned as it is, except one that already created a device.",
        "tags": [
          "enrolments"
        ],
        "requestBody": {
          "content": {
            "application/json": {
              "schema": {
                "$ref": "#/components/schemas/LinkRemoveEnrolment"
              }
            }
          }
        },
        "responses": {
          "200": {
            "description": "Enrolment, cancelled or already ended",
            "content": {
              "application/json": {
                "schema": {
                  "type": "object",
                  "properties": {
                    "data": {
                      "$ref": "#/components/schemas/LinkEnrolment"
                    }
                  },
                  "required": [
                    "data"
                  ]
                }
              }
            }
          },
          "400": {
            "description": "Malformed request",
            "content": {
              "application/problem+json": {
                "schema": {
                  "$ref": "#/components/schemas/Problem"
                }
              }
            }
          },
          "404": {
            "description": "No such enrolment for this secret",
            "content": {
              "application/problem+json": {
                "schema": {
                  "$ref": "#/components/schemas/Problem"
                }
              }
            }
          },
          "409": {
            "description": "The enrolment already created a device",
            "content": {
              "application/problem+json": {
                "schema": {
                  "$ref": "#/components/schemas/Problem"
                }
              }
            }
          }
        }
      }
    },
    "/link/v1/devices/add": {
      "post": {
        "operationId": "addDevice",
        "summary": "Complete an approved enrolment",
        "description": "Called by the agent once enrolments/get reports `approved`. Authenticated with the enrolment secret, and proven with a signature by the enrolling key over the enrolment proof (docs/specs/link.md §6.3):\n\n```\nRSX-LINK-V1-ENROLMENT-PROOF\\n{enrolmentId}\\n{keyFingerprint}\\n{challenge}\n```\n\nCreates the device exactly once. Requires an `Idempotency-Key`; after a lost response, retry with the same key, or read `deviceId` from enrolments/get. Five failed proofs cancel the enrolment.",
        "tags": [
          "devices"
        ],
        "requestBody": {
          "content": {
            "application/json": {
              "schema": {
                "$ref": "#/components/schemas/LinkCreateDevice"
              }
            }
          }
        },
        "responses": {
          "200": {
            "description": "Device enrolled",
            "content": {
              "application/json": {
                "schema": {
                  "type": "object",
                  "properties": {
                    "data": {
                      "$ref": "#/components/schemas/LinkDeviceCreated"
                    }
                  },
                  "required": [
                    "data"
                  ]
                }
              }
            }
          },
          "400": {
            "description": "Malformed request",
            "content": {
              "application/problem+json": {
                "schema": {
                  "$ref": "#/components/schemas/Problem"
                }
              }
            }
          },
          "401": {
            "description": "The signature did not verify",
            "content": {
              "application/problem+json": {
                "schema": {
                  "$ref": "#/components/schemas/Problem"
                }
              }
            }
          },
          "403": {
            "description": "The enrolment was denied",
            "content": {
              "application/problem+json": {
                "schema": {
                  "$ref": "#/components/schemas/Problem"
                }
              }
            }
          },
          "404": {
            "description": "No such enrolment for this secret",
            "content": {
              "application/problem+json": {
                "schema": {
                  "$ref": "#/components/schemas/Problem"
                }
              }
            }
          },
          "409": {
            "description": "Not yet approved, already completed, the key is already a device, or the account has too many devices",
            "content": {
              "application/problem+json": {
                "schema": {
                  "$ref": "#/components/schemas/Problem"
                }
              }
            }
          },
          "410": {
            "description": "The enrolment expired or was cancelled",
            "content": {
              "application/problem+json": {
                "schema": {
                  "$ref": "#/components/schemas/Problem"
                }
              }
            }
          }
        }
      }
    },
    "/link/v1/challenges/add": {
      "post": {
        "operationId": "addChallenge",
        "summary": "Get a challenge to sign for a device session",
        "description": "No credential: a challenge is worthless without the device's key. Valid for 60 seconds and one attempt. Requires an `Idempotency-Key`. Issued for a revoked device too, so that the device learns it was revoked only after proving it holds the key (sessions/add).",
        "tags": [
          "sessions"
        ],
        "requestBody": {
          "content": {
            "application/json": {
              "schema": {
                "$ref": "#/components/schemas/LinkCreateChallenge"
              }
            }
          }
        },
        "responses": {
          "200": {
            "description": "Challenge",
            "content": {
              "application/json": {
                "schema": {
                  "type": "object",
                  "properties": {
                    "data": {
                      "$ref": "#/components/schemas/LinkChallenge"
                    }
                  },
                  "required": [
                    "data"
                  ]
                }
              }
            }
          },
          "400": {
            "description": "Malformed request",
            "content": {
              "application/problem+json": {
                "schema": {
                  "$ref": "#/components/schemas/Problem"
                }
              }
            }
          },
          "404": {
            "description": "No such device",
            "content": {
              "application/problem+json": {
                "schema": {
                  "$ref": "#/components/schemas/Problem"
                }
              }
            }
          }
        }
      }
    },
    "/link/v1/sessions/add": {
      "post": {
        "operationId": "addSession",
        "summary": "Exchange a signed challenge for a device session",
        "description": "The device signs the session proof (docs/specs/link.md §6.3) with its enrolled key:\n\n```\nRSX-LINK-V1-SESSION-PROOF\\n{deviceId}\\n{keyFingerprint}\\n{challengeId}\\n{challenge}\n```\n\nReturns a 15-minute session token, shown once, which is checked against the device on every use. The challenge is consumed by any attempt. Requires an `Idempotency-Key`.",
        "tags": [
          "sessions"
        ],
        "requestBody": {
          "content": {
            "application/json": {
              "schema": {
                "$ref": "#/components/schemas/LinkCreateSession"
              }
            }
          }
        },
        "responses": {
          "200": {
            "description": "Device session",
            "content": {
              "application/json": {
                "schema": {
                  "type": "object",
                  "properties": {
                    "data": {
                      "$ref": "#/components/schemas/LinkSessionCreated"
                    }
                  },
                  "required": [
                    "data"
                  ]
                }
              }
            }
          },
          "400": {
            "description": "Unsupported protocol, or a malformed request",
            "content": {
              "application/problem+json": {
                "schema": {
                  "$ref": "#/components/schemas/Problem"
                }
              }
            }
          },
          "401": {
            "description": "The challenge is unusable, or the signature did not verify",
            "content": {
              "application/problem+json": {
                "schema": {
                  "$ref": "#/components/schemas/Problem"
                }
              }
            }
          },
          "403": {
            "description": "The device is revoked, or its owner's account is not active",
            "content": {
              "application/problem+json": {
                "schema": {
                  "$ref": "#/components/schemas/Problem"
                }
              }
            }
          },
          "503": {
            "description": "RSX ID could not confirm the account; retry",
            "content": {
              "application/problem+json": {
                "schema": {
                  "$ref": "#/components/schemas/Problem"
                }
              }
            }
          }
        }
      }
    },
    "/link/v1/sessions/get": {
      "get": {
        "operationId": "getSession",
        "summary": "Check the current device session",
        "description": "Authenticated with the device session as `Authorization: Bearer rsx_lks_…`. The device-authorised call of B02: it answers only while the session is unexpired and the device is active, and reports a revoked device as `link-device-revoked` from the moment the revocation commits.",
        "tags": [
          "sessions"
        ],
        "responses": {
          "200": {
            "description": "The session and its device",
            "content": {
              "application/json": {
                "schema": {
                  "type": "object",
                  "properties": {
                    "data": {
                      "$ref": "#/components/schemas/LinkSession"
                    }
                  },
                  "required": [
                    "data"
                  ]
                }
              }
            }
          },
          "401": {
            "description": "The session is missing, unknown or expired",
            "content": {
              "application/problem+json": {
                "schema": {
                  "$ref": "#/components/schemas/Problem"
                }
              }
            }
          },
          "403": {
            "description": "The device is revoked",
            "content": {
              "application/problem+json": {
                "schema": {
                  "$ref": "#/components/schemas/Problem"
                }
              }
            }
          }
        }
      }
    },
    "/link/v1/approvals/get": {
      "get": {
        "operationId": "getApproval",
        "summary": "Look up a device waiting for your approval",
        "description": "The person's view of a pending enrolment, by the code the device shows: what is asking, from where, and until when. Only live enrolments, and only ones nobody else has approved.",
        "tags": [
          "approvals"
        ],
        "parameters": [
          {
            "schema": {
              "type": "string",
              "minLength": 8,
              "maxLength": 16,
              "description": "The code shown by the device. Case and hyphens are ignored.",
              "example": "BCDF-GHJK"
            },
            "required": true,
            "name": "code",
            "in": "query"
          }
        ],
        "responses": {
          "200": {
            "description": "The enrolment awaiting a decision",
            "content": {
              "application/json": {
                "schema": {
                  "type": "object",
                  "properties": {
                    "data": {
                      "$ref": "#/components/schemas/LinkApproval"
                    }
                  },
                  "required": [
                    "data"
                  ]
                }
              }
            }
          },
          "400": {
            "description": "The request did not match the endpoint's schema",
            "content": {
              "application/problem+json": {
                "schema": {
                  "$ref": "#/components/schemas/Problem"
                }
              }
            }
          },
          "401": {
            "description": "Missing or invalid RSX ID token, a token issued to another application, or a platform API key",
            "content": {
              "application/problem+json": {
                "schema": {
                  "$ref": "#/components/schemas/Problem"
                }
              }
            }
          },
          "403": {
            "description": "Sign-in is not configured",
            "content": {
              "application/problem+json": {
                "schema": {
                  "$ref": "#/components/schemas/Problem"
                }
              }
            }
          },
          "404": {
            "description": "No live enrolment with that code",
            "content": {
              "application/problem+json": {
                "schema": {
                  "$ref": "#/components/schemas/Problem"
                }
              }
            }
          }
        }
      }
    },
    "/link/v1/approvals/update": {
      "post": {
        "operationId": "updateApproval",
        "summary": "Approve or deny a device",
        "description": "Both the code and the enrolment id are required, so the decision applies to exactly the request the person was shown. Approving makes the caller the device's owner once the device completes enrolment; it is idempotent for the approver. Denying is allowed while pending, and by the approver until the device completes.",
        "tags": [
          "approvals"
        ],
        "requestBody": {
          "content": {
            "application/json": {
              "schema": {
                "$ref": "#/components/schemas/LinkUpdateApproval"
              }
            }
          }
        },
        "responses": {
          "200": {
            "description": "The decision",
            "content": {
              "application/json": {
                "schema": {
                  "type": "object",
                  "properties": {
                    "data": {
                      "$ref": "#/components/schemas/LinkApproval"
                    }
                  },
                  "required": [
                    "data"
                  ]
                }
              }
            }
          },
          "400": {
            "description": "The request did not match the endpoint's schema",
            "content": {
              "application/problem+json": {
                "schema": {
                  "$ref": "#/components/schemas/Problem"
                }
              }
            }
          },
          "401": {
            "description": "Missing or invalid RSX ID token, a token issued to another application, or a platform API key",
            "content": {
              "application/problem+json": {
                "schema": {
                  "$ref": "#/components/schemas/Problem"
                }
              }
            }
          },
          "403": {
            "description": "Sign-in is not configured, or the account is not active",
            "content": {
              "application/problem+json": {
                "schema": {
                  "$ref": "#/components/schemas/Problem"
                }
              }
            }
          },
          "404": {
            "description": "No live enrolment with that code and id",
            "content": {
              "application/problem+json": {
                "schema": {
                  "$ref": "#/components/schemas/Problem"
                }
              }
            }
          },
          "409": {
            "description": "Already decided by someone else, the key is already a device, or too many devices",
            "content": {
              "application/problem+json": {
                "schema": {
                  "$ref": "#/components/schemas/Problem"
                }
              }
            }
          },
          "503": {
            "description": "RSX ID could not confirm the account; nothing was changed",
            "content": {
              "application/problem+json": {
                "schema": {
                  "$ref": "#/components/schemas/Problem"
                }
              }
            }
          }
        }
      }
    },
    "/link/v1/devices/list": {
      "get": {
        "operationId": "listDevices",
        "summary": "List your devices",
        "description": "Your devices, newest first. `status` filters; the default is every device, revoked included.",
        "tags": [
          "devices"
        ],
        "parameters": [
          {
            "schema": {
              "type": "string",
              "enum": [
                "active",
                "revoked",
                "all"
              ],
              "default": "all",
              "example": "all"
            },
            "required": false,
            "name": "status",
            "in": "query"
          },
          {
            "schema": {
              "type": "string",
              "maxLength": 200,
              "example": ""
            },
            "required": false,
            "name": "cursor",
            "in": "query"
          },
          {
            "schema": {
              "type": "integer",
              "minimum": 1,
              "maximum": 100,
              "default": 50,
              "example": 50
            },
            "required": false,
            "name": "limit",
            "in": "query"
          }
        ],
        "responses": {
          "200": {
            "description": "Devices",
            "content": {
              "application/json": {
                "schema": {
                  "type": "object",
                  "properties": {
                    "data": {
                      "type": "array",
                      "items": {
                        "$ref": "#/components/schemas/LinkDevice"
                      }
                    },
                    "meta": {
                      "$ref": "#/components/schemas/LinkPageMeta"
                    }
                  },
                  "required": [
                    "data",
                    "meta"
                  ]
                }
              }
            }
          },
          "400": {
            "description": "The request did not match the endpoint's schema",
            "content": {
              "application/problem+json": {
                "schema": {
                  "$ref": "#/components/schemas/Problem"
                }
              }
            }
          },
          "401": {
            "description": "Missing or invalid RSX ID token, a token issued to another application, or a platform API key",
            "content": {
              "application/problem+json": {
                "schema": {
                  "$ref": "#/components/schemas/Problem"
                }
              }
            }
          },
          "403": {
            "description": "Sign-in is not configured",
            "content": {
              "application/problem+json": {
                "schema": {
                  "$ref": "#/components/schemas/Problem"
                }
              }
            }
          }
        }
      }
    },
    "/link/v1/devices/get": {
      "get": {
        "operationId": "getDevice",
        "summary": "Fetch one of your devices",
        "tags": [
          "devices"
        ],
        "parameters": [
          {
            "schema": {
              "type": "string",
              "format": "uuid",
              "example": "0199d6a1-3b2c-7d4e-8f50-6a7b8c9d0e1f"
            },
            "required": true,
            "name": "id",
            "in": "query"
          }
        ],
        "responses": {
          "200": {
            "description": "Device",
            "content": {
              "application/json": {
                "schema": {
                  "type": "object",
                  "properties": {
                    "data": {
                      "$ref": "#/components/schemas/LinkDevice"
                    }
                  },
                  "required": [
                    "data"
                  ]
                }
              }
            }
          },
          "400": {
            "description": "The request did not match the endpoint's schema",
            "content": {
              "application/problem+json": {
                "schema": {
                  "$ref": "#/components/schemas/Problem"
                }
              }
            }
          },
          "401": {
            "description": "Missing or invalid RSX ID token, a token issued to another application, or a platform API key",
            "content": {
              "application/problem+json": {
                "schema": {
                  "$ref": "#/components/schemas/Problem"
                }
              }
            }
          },
          "403": {
            "description": "Sign-in is not configured",
            "content": {
              "application/problem+json": {
                "schema": {
                  "$ref": "#/components/schemas/Problem"
                }
              }
            }
          },
          "404": {
            "description": "No such device on your account",
            "content": {
              "application/problem+json": {
                "schema": {
                  "$ref": "#/components/schemas/Problem"
                }
              }
            }
          }
        }
      }
    },
    "/link/v1/devices/update": {
      "post": {
        "operationId": "updateDevice",
        "summary": "Rename one of your devices",
        "description": "The display name is the only thing a person may change about a device.",
        "tags": [
          "devices"
        ],
        "requestBody": {
          "content": {
            "application/json": {
              "schema": {
                "$ref": "#/components/schemas/LinkUpdateDevice"
              }
            }
          }
        },
        "responses": {
          "200": {
            "description": "Device",
            "content": {
              "application/json": {
                "schema": {
                  "type": "object",
                  "properties": {
                    "data": {
                      "$ref": "#/components/schemas/LinkDevice"
                    }
                  },
                  "required": [
                    "data"
                  ]
                }
              }
            }
          },
          "400": {
            "description": "The request did not match the endpoint's schema",
            "content": {
              "application/problem+json": {
                "schema": {
                  "$ref": "#/components/schemas/Problem"
                }
              }
            }
          },
          "401": {
            "description": "Missing or invalid RSX ID token, a token issued to another application, or a platform API key",
            "content": {
              "application/problem+json": {
                "schema": {
                  "$ref": "#/components/schemas/Problem"
                }
              }
            }
          },
          "403": {
            "description": "Sign-in is not configured",
            "content": {
              "application/problem+json": {
                "schema": {
                  "$ref": "#/components/schemas/Problem"
                }
              }
            }
          },
          "404": {
            "description": "No such device on your account",
            "content": {
              "application/problem+json": {
                "schema": {
                  "$ref": "#/components/schemas/Problem"
                }
              }
            }
          }
        }
      }
    },
    "/link/v1/devices/remove": {
      "post": {
        "operationId": "removeDevice",
        "summary": "Revoke one of your devices",
        "description": "Irreversible. From the moment this returns, the device can obtain no session and every session it holds is refused with `link-device-revoked`. Its key can never be enrolled again. Repeatable: revoking a revoked device returns it unchanged.",
        "tags": [
          "devices"
        ],
        "requestBody": {
          "content": {
            "application/json": {
              "schema": {
                "$ref": "#/components/schemas/LinkRemoveDevice"
              }
            }
          }
        },
        "responses": {
          "200": {
            "description": "Device, revoked",
            "content": {
              "application/json": {
                "schema": {
                  "type": "object",
                  "properties": {
                    "data": {
                      "$ref": "#/components/schemas/LinkDevice"
                    }
                  },
                  "required": [
                    "data"
                  ]
                }
              }
            }
          },
          "400": {
            "description": "The request did not match the endpoint's schema",
            "content": {
              "application/problem+json": {
                "schema": {
                  "$ref": "#/components/schemas/Problem"
                }
              }
            }
          },
          "401": {
            "description": "Missing or invalid RSX ID token, a token issued to another application, or a platform API key",
            "content": {
              "application/problem+json": {
                "schema": {
                  "$ref": "#/components/schemas/Problem"
                }
              }
            }
          },
          "403": {
            "description": "Sign-in is not configured",
            "content": {
              "application/problem+json": {
                "schema": {
                  "$ref": "#/components/schemas/Problem"
                }
              }
            }
          },
          "404": {
            "description": "No such device on your account",
            "content": {
              "application/problem+json": {
                "schema": {
                  "$ref": "#/components/schemas/Problem"
                }
              }
            }
          }
        }
      }
    }
  },
  "webhooks": {}
}
