{"info": {"title": "Testflinger API", "version": "1.0.0"}, "tags": [{"name": "V1"}, {"name": "Oidc_Api"}], "servers": [{"url": "https://testflinger.ps7.canonical.com/"}], "paths": {"/v1/": {"get": {"parameters": [], "responses": {"200": {"content": {"application/json": {"schema": {}}}, "description": "Successful response"}}, "tags": ["V1"], "summary": "Identify ourselves."}}, "/v1/job": {"get": {"parameters": [], "responses": {"200": {"content": {"application/json": {"schema": {"$ref": "#/components/schemas/Job"}}}, "description": "Successful response"}, "204": {"content": {"application/json": {"schema": {"type": "object", "properties": {}}}}, "description": "No job found"}}, "tags": ["V1"], "summary": "Request a job to run from supported queues.", "description": "The agent must identify itself via the ``agent_name`` cookie. One or more\n``queue`` query parameters must be supplied; the server returns the first\navailable job across those queues.\n\nAny secrets referenced in the job are resolved against the secrets store\nat this point. Secrets that are inaccessible (store unreachable, path not\nfound, or insufficient permissions) are silently resolved to an empty\nstring rather than causing the request to fail. Agents must therefore\nhandle the possibility of empty secret values."}, "post": {"parameters": [], "responses": {"200": {"content": {"application/json": {"schema": {"$ref": "#/components/schemas/JobId"}}}, "description": "Successful response"}, "422": {"content": {"application/json": {"schema": {"$ref": "#/components/schemas/ValidationError"}}}, "description": "Validation error"}}, "tags": ["V1"], "summary": "Add a job to the queue.", "description": "The ``job_queue`` field in the submitted JSON determines which queue the\njob is placed on. All other fields are passed through to the agent\nunchanged.\n\nReturns HTTP 422 if the job references secrets that are inaccessible at\nsubmission time (e.g. the secrets store is unreachable or the secret path\ndoes not exist for the submitting client).", "requestBody": {"content": {"application/json": {"schema": {"$ref": "#/components/schemas/Job"}}}}}}, "/v1/job/search": {"get": {"parameters": [{"in": "query", "name": "tags", "description": "List of tags to search for", "schema": {"type": "array", "items": {"type": "string"}}, "required": false, "explode": true, "style": "form"}, {"in": "query", "name": "match", "description": "Match mode - 'all' or 'any' (default 'any')", "schema": {"type": "string", "enum": ["any", "all"]}, "required": false}, {"in": "query", "name": "state", "description": "List of job states to include", "schema": {"type": "array", "items": {"type": "string", "enum": ["setup", "provision", "firmware_update", "test", "allocate", "allocated", "reserve", "cleanup", "cancelled", "completed", "active"]}}, "required": false, "explode": true, "style": "form"}], "responses": {"200": {"content": {"application/json": {"schema": {"$ref": "#/components/schemas/JobSearchResponse"}}}, "description": "Successful response"}, "422": {"content": {"application/json": {"schema": {"$ref": "#/components/schemas/ValidationError"}}}, "description": "Validation error"}}, "tags": ["V1"], "summary": "Search for jobs by tags."}}, "/v1/agents/data": {"get": {"parameters": [], "responses": {"200": {"content": {"application/json": {"schema": {"$ref": "#/components/schemas/AgentOut"}}}, "description": "Successful response"}}, "tags": ["V1"], "summary": "Get all agent data."}}, "/oidc/auth-init": {"post": {"parameters": [], "responses": {"200": {"content": {"application/json": {"schema": {}}}, "description": "Successful response"}}, "tags": ["Oidc_Api"], "summary": "Initiate client request and proxy request to OIDC provider.", "description": ":return: dict containing required OIDC parameters for client to\nauthenticate with provider"}}, "/v1/job/{job_id}": {"get": {"parameters": [{"in": "path", "name": "job_id", "schema": {"type": "string"}, "required": true}], "responses": {"200": {"content": {"application/json": {"schema": {"$ref": "#/components/schemas/JobOut"}}}, "description": "Successful response"}, "404": {"content": {"application/json": {"schema": {"$ref": "#/components/schemas/HTTPError"}}}, "description": "Not found"}}, "tags": ["V1"], "summary": "Request the json job definition for a specified job, even if it has", "description": "already run.\n\n:param job_id:\nUUID as a string for the job\n:return:\nJSON data for the job or error string and http error"}}, "/v1/oauth2/token": {"post": {"parameters": [], "responses": {"200": {"content": {"application/json": {"schema": {}}}, "description": "Successful response"}}, "tags": ["V1"], "summary": "Issue both access token and refresh token for a client.", "description": "Get JWT with priority and queue permissions.\n\nBefore being encrypted, the JWT can contain fields like:\n{\nexp: ,\niat: ,\nsub: ,\npermissions: {\nmax_priority: ,\nallowed_queues: ,\nmax_reservation_time: ,\n}\n}"}}, "/v1/agents/queues": {"get": {"parameters": [], "responses": {"200": {"content": {"application/json": {"schema": {"type": "object", "additionalProperties": {"type": "string"}, "example": {"device001": "Queue for device001", "some-queue": "some other queue"}}}}, "description": "Mapping of queue names and descriptions"}}, "tags": ["V1"], "summary": "Get all advertised queues from this server.", "description": "Returns a dict of queue names and descriptions, ex:\n{\n\"some_queue\": \"A queue for testing\",\n\"other_queue\": \"A queue for something else\"\n}"}, "post": {"parameters": [], "responses": {"200": {"content": {"application/json": {"schema": {}}}, "description": "Successful response"}, "422": {"content": {"application/json": {"schema": {"$ref": "#/components/schemas/ValidationError"}}}, "description": "Validation error"}}, "tags": ["V1"], "summary": "Tell testflinger the queue names that are being serviced.", "description": "Some agents may want to advertise some of the queues they listen on so that\nthe user can check which queues are valid to use.", "requestBody": {"content": {"application/json": {"schema": {"$ref": "#/components/schemas/QueuesIn"}}}}}}, "/v1/agents/images": {"post": {"parameters": [], "responses": {"200": {"content": {"application/json": {"schema": {}}}, "description": "Successful response"}, "422": {"content": {"application/json": {"schema": {"$ref": "#/components/schemas/ValidationError"}}}, "description": "Validation error"}}, "tags": ["V1"], "summary": "Tell testflinger about known images for a specified queue", "description": "images will be stored in a dict of key/value pairs as part of the queues\ncollection. That dict will contain image_name:provision_data mappings, ex:\n{\n\"some_queue\": {\n\"core22\": \"http://cdimage.ubuntu.com/.../core-22.tar.gz\",\n\"jammy\": \"http://cdimage.ubuntu.com/.../ubuntu-22.04.tar.gz\"\n},\n\"other_queue\": {\n...\n}\n}.", "requestBody": {"content": {"application/json": {"schema": {"$ref": "#/components/schemas/ImagesIn"}}}}}}, "/v1/oauth2/revoke": {"post": {"parameters": [], "responses": {"200": {"content": {"application/json": {"schema": {}}}, "description": "Successful response"}, "422": {"content": {"application/json": {"schema": {"$ref": "#/components/schemas/ValidationError"}}}, "description": "Validation error"}}, "tags": ["V1"], "summary": "Revoke a refresh token. Only admins can perform this action.", "requestBody": {"content": {"application/json": {"schema": {"$ref": "#/components/schemas/RefreshTokenIn"}}}}}}, "/v1/oauth2/refresh": {"post": {"parameters": [], "responses": {"200": {"content": {"application/json": {"schema": {}}}, "description": "Successful response"}, "422": {"content": {"application/json": {"schema": {"$ref": "#/components/schemas/ValidationError"}}}, "description": "Validation error"}}, "tags": ["V1"], "summary": "Refresh access token using a valid refresh token.", "requestBody": {"content": {"application/json": {"schema": {"$ref": "#/components/schemas/RefreshTokenIn"}}}}}}, "/v1/result/{job_id}": {"get": {"parameters": [{"in": "path", "name": "job_id", "schema": {"type": "string"}, "required": true}], "responses": {"200": {"content": {"application/json": {"schema": {"$ref": "#/components/schemas/ResultGet"}}}, "description": "Successful response"}, "404": {"content": {"application/json": {"schema": {"$ref": "#/components/schemas/HTTPError"}}}, "description": "Not found"}, "204": {"content": {}, "description": "No result found"}}, "tags": ["V1"], "summary": "Return results for a specified job_id.", "description": "Results are reconstructed from the log storage system to maintain\nbackward compatibility. Phase exit codes are combined with captured\nlog data and returned as a flat structure:\n\n- ``{phase}_status``: exit code for each phase\n- ``{phase}_output``: stdout log for that phase (if available)\n- ``{phase}_serial``: serial console log for that phase (if available)\n- Additional metadata fields such as ``device_info`` and ``job_state``\n\n:param job_id: UUID as a string for the job\n:raises HTTPError: If the job_id is not a valid UUID"}, "post": {"parameters": [{"in": "path", "name": "job_id", "schema": {"type": "string"}, "required": true}], "responses": {"200": {"content": {"application/json": {"schema": {}}}, "description": "Successful response"}, "422": {"content": {"application/json": {"schema": {"$ref": "#/components/schemas/ValidationError"}}}, "description": "Validation error"}, "404": {"content": {"application/json": {"schema": {"$ref": "#/components/schemas/HTTPError"}}}, "description": "Not found"}}, "tags": ["V1"], "summary": "Post a result for a specified job_id.", "description": ":param job_id: UUID as a string for the job\n:raises HTTPError: If the job_id is not a valid UUID", "requestBody": {"content": {"application/json": {"schema": {"$ref": "#/components/schemas/ResultPost"}}}}}}, "/v1/queues/wait_times": {"get": {"parameters": [], "responses": {"200": {"content": {"application/json": {"schema": {}}}, "description": "Successful response"}}, "tags": ["V1"], "summary": "Get wait time metrics - optionally take a list of queues."}}, "/v1/restricted-queues": {"get": {"parameters": [], "responses": {"200": {"content": {"application/json": {"schema": {"$ref": "#/components/schemas/RestrictedQueueOut"}}}, "description": "Successful response"}}, "tags": ["V1"], "summary": "List all agent's restricted queues and its owners."}}, "/v1/client-permissions": {"get": {"parameters": [], "responses": {"200": {"content": {"application/json": {"schema": {"$ref": "#/components/schemas/ClientPermissionsOut"}}}, "description": "Successful response"}}, "tags": ["V1"], "summary": "Retrieve all client permissions from database."}}, "/v1/job/{job_id}/action": {"post": {"parameters": [{"in": "path", "name": "job_id", "schema": {"type": "string"}, "required": true}], "responses": {"200": {"content": {"application/json": {"schema": {}}}, "description": "Successful response"}, "422": {"content": {"application/json": {"schema": {"$ref": "#/components/schemas/ValidationError"}}}, "description": "Validation error"}, "404": {"content": {"application/json": {"schema": {"$ref": "#/components/schemas/HTTPError"}}}, "description": "Not found"}}, "tags": ["V1"], "summary": "Take action on the job status for a specified job ID.", "description": ":param job_id:\nUUID as a string for the job", "requestBody": {"content": {"application/json": {"schema": {"$ref": "#/components/schemas/ActionIn"}}}}}}, "/v1/job/{job_id}/events": {"post": {"parameters": [{"in": "path", "name": "job_id", "schema": {"type": "string"}, "required": true}], "responses": {"200": {"content": {"application/json": {"schema": {}}}, "description": "Successful response"}, "422": {"content": {"application/json": {"schema": {"$ref": "#/components/schemas/ValidationError"}}}, "description": "Validation error"}, "404": {"content": {"application/json": {"schema": {"$ref": "#/components/schemas/HTTPError"}}}, "description": "Not found"}}, "tags": ["V1"], "summary": "Post status updates from the agent to the server to be forwarded", "description": "to the server-configured webhook url.\n\nThe json sent to this endpoint may contain data such as the following:\n{\n\"agent_id\": \"\",\n\"job_queue\": \"\",\n\"job_status_webhook\": \"\",\n\"events\": [\n{\n\"event_name\": \"\",\n\"timestamp\": \"\",\n\"detail\": \"\"\n},\n...\n]\n}\n\n:param job_id: UUID as a string for the job\n:param json_data: JSON data containing the status updates and webhook URL", "requestBody": {"content": {"application/json": {"schema": {"$ref": "#/components/schemas/StatusUpdate"}}}}}}, "/v1/agents/images/{queue}": {"get": {"parameters": [{"in": "path", "name": "queue", "schema": {"type": "string"}, "required": true}], "responses": {"404": {"content": {"application/json": {"schema": {"$ref": "#/components/schemas/HTTPError"}}}, "description": "Not found"}, "200": {"content": {"application/json": {"schema": {"type": "object", "additionalProperties": {"type": "string"}, "example": {"core22": "url: http://.../core22.img.xz", "server-22.04": "url: http://.../ubuntu-22.04.img.xz"}}}}, "description": "Mapping of image names and provision data"}}, "tags": ["V1"], "summary": "Get a dict of known images for a given queue."}}, "/v1/job/{job_id}/position": {"get": {"parameters": [{"in": "path", "name": "job_id", "schema": {"type": "string"}, "required": true}], "responses": {"200": {"content": {"application/json": {"schema": {}}}, "description": "Successful response"}, "404": {"content": {"application/json": {"schema": {"$ref": "#/components/schemas/HTTPError"}}}, "description": "Not found"}}, "tags": ["V1"], "summary": "Return the position of the specified jobid in the queue."}}, "/v1/result/{job_id}/status": {"get": {"parameters": [{"in": "path", "name": "job_id", "schema": {"type": "string"}, "required": true}], "responses": {"200": {"content": {"application/json": {"schema": {"$ref": "#/components/schemas/ResultStatus"}}}, "description": "Successful response"}, "404": {"content": {"application/json": {"schema": {"$ref": "#/components/schemas/HTTPError"}}}, "description": "Not found"}, "204": {"content": {}, "description": "No result found"}}, "tags": ["V1"], "summary": "Return job state and phase exit codes for a specified job_id.", "description": "This is a lightweight alternative to GET /result/ that omits\nlog data (output and serial). Use this when only the job state or\nphase statuses are needed.\n\n:param job_id: UUID as a string for the job\n:raises HTTPError: If the job_id is not a valid UUID"}}, "/v1/job/{job_id}/attachments": {"get": {"parameters": [{"in": "path", "name": "job_id", "schema": {"type": "string"}, "required": true}], "responses": {"200": {"content": {"application/json": {"schema": {}}}, "description": "Successful response"}, "404": {"content": {"application/json": {"schema": {"$ref": "#/components/schemas/HTTPError"}}}, "description": "Not found"}}, "tags": ["V1"], "summary": "Return the attachments bundle for a specified job_id.", "description": ":param job_id:\nUUID as a string for the job\n:return:\nsend_file stream of attachment tarball to download"}, "post": {"parameters": [{"in": "path", "name": "job_id", "schema": {"type": "string"}, "required": true}], "responses": {"200": {"content": {"application/json": {"schema": {}}}, "description": "Successful response"}, "404": {"content": {"application/json": {"schema": {"$ref": "#/components/schemas/HTTPError"}}}, "description": "Not found"}}, "tags": ["V1"], "summary": "Post attachment bundle for a specified job_id.", "description": ":param job_id:\nUUID as a string for the job"}}, "/v1/result/{job_id}/artifact": {"get": {"parameters": [{"in": "path", "name": "job_id", "schema": {"type": "string"}, "required": true}], "responses": {"200": {"content": {"application/json": {"schema": {}}}, "description": "Successful response"}, "404": {"content": {"application/json": {"schema": {"$ref": "#/components/schemas/HTTPError"}}}, "description": "Not found"}}, "tags": ["V1"], "summary": "Return artifact bundle for a specified job_id.", "description": ":param job_id:\nUUID as a string for the job\n:return:\nsend_file stream of artifact tarball to download"}, "post": {"parameters": [{"in": "path", "name": "job_id", "schema": {"type": "string"}, "required": true}], "responses": {"200": {"content": {"application/json": {"schema": {}}}, "description": "Successful response"}, "404": {"content": {"application/json": {"schema": {"$ref": "#/components/schemas/HTTPError"}}}, "description": "Not found"}}, "tags": ["V1"], "summary": "Post artifact bundle for a specified job_id.", "description": ":param job_id:\nUUID as a string for the job"}}, "/v1/agents/data/{agent_name}": {"get": {"parameters": [{"in": "path", "name": "agent_name", "schema": {"type": "string"}, "required": true}], "responses": {"200": {"content": {"application/json": {"schema": {"$ref": "#/components/schemas/AgentOut"}}}, "description": "Successful response"}, "404": {"content": {"application/json": {"schema": {"$ref": "#/components/schemas/HTTPError"}}}, "description": "Not found"}}, "tags": ["V1"], "summary": "Get the information from a specified agent.", "description": ":param agent_name:\nString with the name of the agent to retrieve information from.\n:return:\nJSON data with the specified agent information."}, "post": {"parameters": [{"in": "path", "name": "agent_name", "schema": {"type": "string"}, "required": true}], "responses": {"200": {"content": {"application/json": {"schema": {}}}, "description": "Successful response"}, "422": {"content": {"application/json": {"schema": {"$ref": "#/components/schemas/ValidationError"}}}, "description": "Validation error"}, "404": {"content": {"application/json": {"schema": {"$ref": "#/components/schemas/HTTPError"}}}, "description": "Not found"}}, "tags": ["V1"], "summary": "Post information about the agent to the server.", "description": "The json sent to this endpoint may contain data such as the following:\n{\n\"state\": string, # State the device is in\n\"queues\": array[string], # Queues the device is listening on\n\"location\": string, # Location of the device\n\"job_id\": string, # Job ID the device is running, if any\n\"log\": array[string], # push and keep only the last 100 lines\n}", "requestBody": {"content": {"application/json": {"schema": {"$ref": "#/components/schemas/AgentIn"}}}}}}, "/v1/queues/{queue_name}/jobs": {"get": {"parameters": [{"in": "path", "name": "queue_name", "schema": {"type": "string"}, "required": true}], "responses": {"200": {"content": {"application/json": {"schema": {}}}, "description": "Successful response"}, "404": {"content": {"application/json": {"schema": {"$ref": "#/components/schemas/HTTPError"}}}, "description": "Not found"}}, "tags": ["V1"], "summary": "Get the jobs in a specified queue along with its state.", "description": ":param queue_name\nString with the queue name where to perform the query.\n:return:\nJSON data with the jobs allocated to the specified queue."}}, "/oidc/auth-poll/{request_id}": {"post": {"parameters": [{"in": "path", "name": "request_id", "schema": {"type": "string"}, "required": true}], "responses": {"200": {"content": {"application/json": {"schema": {}}}, "description": "Successful response"}, "404": {"content": {"application/json": {"schema": {"$ref": "#/components/schemas/HTTPError"}}}, "description": "Not found"}}, "tags": ["Oidc_Api"], "summary": "Poll for OIDC authentication result based on request ID.", "description": ":param request_id: unique request ID generated during auth initiation\n:return: dict containing authentication result and user info if successful"}}, "/v1/queues/{queue_name}/agents": {"get": {"parameters": [{"in": "path", "name": "queue_name", "schema": {"type": "string"}, "required": true}], "responses": {"200": {"content": {"application/json": {"schema": {"$ref": "#/components/schemas/AgentOut"}}}, "description": "Successful response"}, "404": {"content": {"application/json": {"schema": {"$ref": "#/components/schemas/HTTPError"}}}, "description": "Not found"}}, "tags": ["V1"], "summary": "Get the list of all data for agents listening to a specified queue."}}, "/v1/restricted-queues/{queue_name}": {"get": {"parameters": [{"in": "path", "name": "queue_name", "schema": {"type": "string"}, "required": true}], "responses": {"200": {"content": {"application/json": {"schema": {"$ref": "#/components/schemas/RestrictedQueueOut"}}}, "description": "Successful response"}, "404": {"content": {"application/json": {"schema": {"$ref": "#/components/schemas/HTTPError"}}}, "description": "Not found"}}, "tags": ["V1"], "summary": "Get restricted queues for a specific agent."}, "post": {"parameters": [{"in": "path", "name": "queue_name", "schema": {"type": "string"}, "required": true}], "responses": {"200": {"content": {"application/json": {"schema": {}}}, "description": "Successful response"}, "422": {"content": {"application/json": {"schema": {"$ref": "#/components/schemas/ValidationError"}}}, "description": "Validation error"}, "404": {"content": {"application/json": {"schema": {"$ref": "#/components/schemas/HTTPError"}}}, "description": "Not found"}}, "tags": ["V1"], "summary": "Add an owner to the specific restricted queue.", "requestBody": {"content": {"application/json": {"schema": {"$ref": "#/components/schemas/RestrictedQueueIn"}}}}}, "delete": {"parameters": [{"in": "path", "name": "queue_name", "schema": {"type": "string"}, "required": true}], "responses": {"200": {"content": {"application/json": {"schema": {}}}, "description": "Successful response"}, "422": {"content": {"application/json": {"schema": {"$ref": "#/components/schemas/ValidationError"}}}, "description": "Validation error"}, "404": {"content": {"application/json": {"schema": {"$ref": "#/components/schemas/HTTPError"}}}, "description": "Not found"}}, "tags": ["V1"], "summary": "Delete an owner from the specific restricted queue.", "requestBody": {"content": {"application/json": {"schema": {"$ref": "#/components/schemas/RestrictedQueueIn"}}}}}}, "/v1/client-permissions/{client_id}": {"get": {"parameters": [{"in": "path", "name": "client_id", "schema": {"type": "string"}, "required": true}], "responses": {"200": {"content": {"application/json": {"schema": {"$ref": "#/components/schemas/ClientPermissionsOut"}}}, "description": "Successful response"}, "404": {"content": {"application/json": {"schema": {"$ref": "#/components/schemas/HTTPError"}}}, "description": "Not found"}}, "tags": ["V1"], "summary": "Retrieve single client-permissions from database."}, "put": {"parameters": [{"in": "path", "name": "client_id", "schema": {"type": "string"}, "required": true}], "responses": {"200": {"content": {"application/json": {"schema": {}}}, "description": "Successful response"}, "422": {"content": {"application/json": {"schema": {"$ref": "#/components/schemas/ValidationError"}}}, "description": "Validation error"}, "404": {"content": {"application/json": {"schema": {"$ref": "#/components/schemas/HTTPError"}}}, "description": "Not found"}}, "tags": ["V1"], "summary": "Add or create client permissions for a specified user.", "requestBody": {"content": {"application/json": {"schema": {"$ref": "#/components/schemas/ClientPermissionsIn"}}}}}, "delete": {"parameters": [{"in": "path", "name": "client_id", "schema": {"type": "string"}, "required": true}], "responses": {"200": {"content": {"application/json": {"schema": {}}}, "description": "Successful response"}, "404": {"content": {"application/json": {"schema": {"$ref": "#/components/schemas/HTTPError"}}}, "description": "Not found"}}, "tags": ["V1"], "summary": "Delete client id along with its permissions."}}, "/v1/secrets/{client_id}/{path}": {"put": {"parameters": [{"in": "path", "name": "client_id", "schema": {"type": "string"}, "required": true}, {"in": "path", "name": "path", "schema": {"type": "string"}, "required": true}], "responses": {"200": {"content": {"application/json": {"schema": {"$ref": "#/components/schemas/SecretOut"}}}, "description": "Successful response"}, "422": {"content": {"application/json": {"schema": {"$ref": "#/components/schemas/ValidationError"}}}, "description": "Validation error"}, "404": {"content": {"application/json": {"schema": {"$ref": "#/components/schemas/HTTPError"}}}, "description": "Not found"}}, "tags": ["V1"], "summary": "Store a secret value for the specified client_id and path.", "requestBody": {"content": {"application/json": {"schema": {"$ref": "#/components/schemas/SecretIn"}}}}}, "delete": {"parameters": [{"in": "path", "name": "client_id", "schema": {"type": "string"}, "required": true}, {"in": "path", "name": "path", "schema": {"type": "string"}, "required": true}], "responses": {"200": {"content": {"application/json": {"schema": {}}}, "description": "Successful response"}, "404": {"content": {"application/json": {"schema": {"$ref": "#/components/schemas/HTTPError"}}}, "description": "Not found"}}, "tags": ["V1"], "summary": "Remove a secret value for the specified client_id and path."}}, "/v1/agents/provision_logs/{agent_name}": {"post": {"parameters": [{"in": "path", "name": "agent_name", "schema": {"type": "string"}, "required": true}], "responses": {"200": {"content": {"application/json": {"schema": {}}}, "description": "Successful response"}, "422": {"content": {"application/json": {"schema": {"$ref": "#/components/schemas/ValidationError"}}}, "description": "Validation error"}, "404": {"content": {"application/json": {"schema": {"$ref": "#/components/schemas/HTTPError"}}}, "description": "Not found"}}, "tags": ["V1"], "summary": "Post provision logs for the agent to the server.", "requestBody": {"content": {"application/json": {"schema": {"$ref": "#/components/schemas/ProvisionLogsIn"}}}}}}, "/v1/result/{job_id}/log/{log_type}": {"get": {"parameters": [{"in": "path", "name": "job_id", "schema": {"type": "string"}, "required": true}, {"in": "path", "name": "log_type", "schema": {"type": "string"}, "required": true}], "responses": {"200": {"content": {"application/json": {"schema": {"$ref": "#/components/schemas/LogGet"}}}, "description": "Successful response"}, "404": {"content": {"application/json": {"schema": {"$ref": "#/components/schemas/HTTPError"}}}, "description": "Not found"}}, "tags": ["V1"], "summary": "Get logs for a specified job_id.", "description": "Logs are persistent and may be retrieved multiple times. Results are\norganised by phase. Each phase entry contains:\n\n- ``last_fragment_number``: highest fragment number stored for that phase\n- ``log_data``: combined log text from all matching fragments\n\nOptional query parameters for filtering:\n\n- ``phase``: restrict results to a single test phase\n- ``start_fragment``: return only fragments from this number onwards\n- ``start_timestamp``: return only fragments created after this\nISO 8601 timestamp\n\n:param job_id: UUID as a string for the job\n:param log_type: LogType enum value for the type of log requested\n:raises HTTPError: If the job_id is not a valid UUID or if invalid query\n:return: Dictionary with log data"}, "post": {"parameters": [{"in": "path", "name": "job_id", "schema": {"type": "string"}, "required": true}, {"in": "path", "name": "log_type", "schema": {"type": "string"}, "required": true}], "responses": {"200": {"content": {"application/json": {"schema": {}}}, "description": "Successful response"}, "422": {"content": {"application/json": {"schema": {"$ref": "#/components/schemas/ValidationError"}}}, "description": "Validation error"}, "404": {"content": {"application/json": {"schema": {"$ref": "#/components/schemas/HTTPError"}}}, "description": "Not found"}}, "tags": ["V1"], "summary": "Post logs for a specified job ID.", "description": "Agents stream log data in sequential fragments. Each request must\ninclude:\n\n- ``fragment_number``: sequential integer starting from 0\n- ``timestamp``: ISO 8601 timestamp when the fragment was created\n- ``phase``: test phase name (setup, provision, firmware_update, test,\nallocate, reserve, cleanup)\n- ``log_data``: the log content for this fragment\n\n:param job_id: UUID as a string for the job\n:param log_type: LogType enum value for the type of log being posted\n:raises HTTPError: If the job_id is not a valid UUID\n:param json_data: Dictionary with log data", "requestBody": {"content": {"application/json": {"schema": {"$ref": "#/components/schemas/LogPost"}}}}}}}, "openapi": "3.0.3", "components": {"schemas": {"JobId": {"type": "object", "properties": {"job_id": {"type": "string"}}, "required": ["job_id"], "additionalProperties": false}, "ValidationError": {"properties": {"detail": {"type": "object", "properties": {"": {"type": "object", "properties": {"": {"type": "array", "items": {"type": "string"}}}}}}, "message": {"type": "string"}}, "type": "object"}, "ProvisionData": {"type": "object", "properties": {}, "additionalProperties": false}, "Attachment": {"type": "object", "properties": {"agent": {"type": "string"}, "device": {"type": "string"}}, "required": ["agent"], "additionalProperties": false}, "TestData": {"type": "object", "properties": {"test_cmds": {"type": "string"}, "attachments": {"type": "array", "items": {"$ref": "#/components/schemas/Attachment"}}, "test_username": {"type": "string"}, "test_password": {"type": "string"}, "secrets": {"type": "object", "additionalProperties": {"type": "string", "pattern": "^[a-zA-Z0-9_/-]+$"}}}, "additionalProperties": false}, "ReserveData": {"type": "object", "properties": {"ssh_keys": {"type": "array", "items": {"type": "string", "pattern": "^(lp|gh):(\\S+)$"}}, "timeout": {"nullable": true}}, "additionalProperties": false}, "Job": {"type": "object", "properties": {"job_id": {"type": "string"}, "parent_job_id": {"type": "string"}, "name": {"type": "string"}, "tags": {"type": "array", "items": {"type": "string"}}, "job_queue": {"type": "string", "minLength": 1}, "global_timeout": {"type": "integer"}, "output_timeout": {"type": "integer"}, "allocation_timeout": {"type": "integer"}, "provision_data": {"anyOf": [{"type": "object", "nullable": true}, {"$ref": "#/components/schemas/ProvisionData"}]}, "firmware_update_data": {"type": "object", "additionalProperties": {}}, "test_data": {"$ref": "#/components/schemas/TestData"}, "allocate_data": {"type": "object", "additionalProperties": {}}, "reserve_data": {"$ref": "#/components/schemas/ReserveData"}, "job_status_webhook": {"type": "string"}, "job_priority": {"type": "integer"}, "exclude_agents": {"type": "array", "items": {"type": "string"}}, "debug": {"type": "boolean"}}, "required": ["job_queue"], "additionalProperties": false}, "JobSearchResponse": {"type": "object", "properties": {"jobs": {"type": "array", "items": {"$ref": "#/components/schemas/Job"}}}, "required": ["jobs"], "additionalProperties": false}, "AgentJob": {"type": "object", "properties": {"job_id": {"type": "string"}, "submitted_by": {"type": "string", "default": null, "nullable": true}, "created_at": {"type": "string", "format": "date-time"}, "started_at": {"type": "string", "format": "date-time"}, "job_queue": {"type": "string"}, "job_state": {"type": "string"}, "job_priority": {"type": "integer"}, "tags": {"type": "array", "items": {"type": "string"}}}, "required": ["job_id"], "additionalProperties": false}, "AgentOut": {"type": "object", "properties": {"name": {"type": "string"}, "job_id": {"type": "string"}, "state": {"type": "string"}, "queues": {"type": "array", "items": {"type": "string"}}, "location": {"type": "string"}, "provision_type": {"type": "string"}, "comment": {"type": "string"}, "restricted_to": {"type": "object", "additionalProperties": {}}, "job": {"anyOf": [{"type": "object", "nullable": true}, {"$ref": "#/components/schemas/AgentJob"}]}}, "required": ["name"], "additionalProperties": false}, "JobOut": {"type": "object", "properties": {"job_id": {"type": "string"}, "parent_job_id": {"type": "string"}, "name": {"type": "string"}, "tags": {"type": "array", "items": {"type": "string"}}, "job_queue": {"type": "string", "minLength": 1}, "global_timeout": {"type": "integer"}, "output_timeout": {"type": "integer"}, "allocation_timeout": {"type": "integer"}, "provision_data": {"anyOf": [{"type": "object", "nullable": true}, {"$ref": "#/components/schemas/ProvisionData"}]}, "firmware_update_data": {"type": "object", "additionalProperties": {}}, "test_data": {"$ref": "#/components/schemas/TestData"}, "allocate_data": {"type": "object", "additionalProperties": {}}, "reserve_data": {"$ref": "#/components/schemas/ReserveData"}, "job_status_webhook": {"type": "string"}, "job_priority": {"type": "integer"}, "exclude_agents": {"type": "array", "items": {"type": "string"}}, "debug": {"type": "boolean"}, "submitted_by": {"type": "string", "default": null, "nullable": true}}, "required": ["job_queue"], "additionalProperties": false}, "HTTPError": {"properties": {"detail": {"type": "object"}, "message": {"type": "string"}}, "type": "object"}, "QueuesIn": {"type": "object", "properties": {}, "additionalProperties": true}, "ImagesIn": {"type": "object", "properties": {}, "additionalProperties": true}, "RefreshTokenIn": {"type": "object", "properties": {"refresh_token": {"type": "string", "minLength": 1}}, "required": ["refresh_token"], "additionalProperties": false}, "ResultPost": {"type": "object", "properties": {"status": {"type": "object", "additionalProperties": {"type": "integer"}}, "agent_id": {"type": "string"}, "device_info": {"type": "object", "additionalProperties": {}}, "job_state": {"type": "string"}}, "additionalProperties": false}, "ResultGet": {"type": "object", "properties": {"setup_status": {"type": "integer"}, "provision_status": {"type": "integer"}, "firmware_update_status": {"type": "integer"}, "test_status": {"type": "integer"}, "allocate_status": {"type": "integer"}, "reserve_status": {"type": "integer"}, "cleanup_status": {"type": "integer"}, "job_state": {"type": "string"}, "setup_output": {"type": "string"}, "setup_serial": {"type": "string"}, "provision_output": {"type": "string"}, "provision_serial": {"type": "string"}, "firmware_update_output": {"type": "string"}, "firmware_update_serial": {"type": "string"}, "test_output": {"type": "string"}, "test_serial": {"type": "string"}, "allocate_output": {"type": "string"}, "allocate_serial": {"type": "string"}, "reserve_output": {"type": "string"}, "reserve_serial": {"type": "string"}, "cleanup_output": {"type": "string"}, "cleanup_serial": {"type": "string"}, "device_info": {"type": "object", "additionalProperties": {}}, "cancelled_by": {"type": "string", "default": null, "nullable": true}}, "additionalProperties": false}, "RestrictedQueueOut": {"type": "object", "properties": {"queue": {"type": "string"}, "owners": {"type": "array", "items": {"type": "string"}}}, "required": ["owners", "queue"], "additionalProperties": false}, "ClientPermissionsOut": {"type": "object", "properties": {"client_id": {"type": "string"}, "max_priority": {"type": "object", "additionalProperties": {"type": "integer"}, "nullable": true}, "allowed_queues": {"type": "array", "items": {"type": "string"}, "nullable": true}, "max_reservation_time": {"type": "object", "additionalProperties": {}, "nullable": true}, "role": {"type": "string"}, "email": {"type": "string", "format": "email", "nullable": true}}, "required": ["allowed_queues", "client_id", "max_priority", "max_reservation_time", "role"], "additionalProperties": false}, "ActionIn": {"type": "object", "properties": {"action": {"type": "string", "enum": ["cancel"]}}, "required": ["action"], "additionalProperties": false}, "JobEvent": {"type": "object", "properties": {"event_name": {"type": "string"}, "timestamp": {"type": "string"}, "detail": {"type": "string"}}, "required": ["event_name", "timestamp"], "additionalProperties": false}, "StatusUpdate": {"type": "object", "properties": {"agent_id": {"type": "string"}, "job_queue": {"type": "string"}, "job_status_webhook": {"type": "string", "format": "url"}, "events": {"type": "array", "items": {"$ref": "#/components/schemas/JobEvent"}}}, "required": ["job_status_webhook"], "additionalProperties": false}, "ResultStatus": {"type": "object", "properties": {"setup_status": {"type": "integer"}, "provision_status": {"type": "integer"}, "firmware_update_status": {"type": "integer"}, "test_status": {"type": "integer"}, "allocate_status": {"type": "integer"}, "reserve_status": {"type": "integer"}, "cleanup_status": {"type": "integer"}, "job_state": {"type": "string"}}, "additionalProperties": false}, "AgentIn": {"type": "object", "properties": {"identifier": {"type": "string"}, "job_id": {"type": "string"}, "location": {"type": "string"}, "log": {"type": "array", "items": {"type": "string"}}, "provision_type": {"type": "string"}, "queues": {"type": "array", "items": {"type": "string"}}, "state": {"type": "string"}, "comment": {"type": "string"}}, "additionalProperties": false}, "RestrictedQueueIn": {"type": "object", "properties": {"client_id": {"type": "string"}}, "required": ["client_id"], "additionalProperties": false}, "ClientPermissionsIn": {"type": "object", "properties": {"client_secret": {"type": "string", "minLength": 15}, "max_priority": {"type": "object", "additionalProperties": {"type": "integer"}}, "max_reservation_time": {"type": "object", "additionalProperties": {"type": "integer"}}, "role": {"type": "string", "enum": ["admin", "manager", "contributor", "agent"]}, "email": {"type": "string", "format": "email"}}, "additionalProperties": false}, "SecretOut": {"type": "object", "properties": {"expires_at": {"type": "string", "format": "date-time", "description": "UTC datetime for secret expiration if TTL is set.", "nullable": true}}, "additionalProperties": false}, "SecretIn": {"type": "object", "properties": {"value": {"type": "string", "minLength": 1}, "expire_after": {"type": "integer", "minimum": 1, "nullable": true}, "ephemeral": {"type": "boolean", "default": false}}, "required": ["value"], "additionalProperties": false}, "ProvisionLogsIn": {"type": "object", "properties": {"job_id": {"type": "string"}, "exit_code": {"type": "integer"}, "detail": {"type": "string"}}, "required": ["exit_code", "job_id"], "additionalProperties": false}, "LogGetItem": {"type": "object", "properties": {"last_fragment_number": {"type": "integer"}, "log_data": {"type": "string"}}, "required": ["last_fragment_number", "log_data"], "additionalProperties": false}, "LogGet": {"type": "object", "properties": {"output": {"type": "object", "additionalProperties": {"$ref": "#/components/schemas/LogGetItem"}}, "serial": {"type": "object", "additionalProperties": {"$ref": "#/components/schemas/LogGetItem"}}}, "additionalProperties": false}, "LogPost": {"type": "object", "properties": {"fragment_number": {"type": "integer"}, "timestamp": {"type": "string", "format": "date-time"}, "phase": {"type": "string", "enum": ["setup", "provision", "firmware_update", "test", "allocate", "reserve", "cleanup"]}, "log_data": {"type": "string"}}, "required": ["fragment_number", "log_data", "phase", "timestamp"], "additionalProperties": false}}}}