{"openapi":"3.1.0","info":{"title":"player.schule API","version":"1.0.0","description":"Manage your own videos on player.schule: list, upload, add YouTube or Vimeo links, import other links, rename, protect with a password, delete. Authenticate with a personal API token (Profile → API tokens) as \"Authorization: Bearer <token>\". Each operation names the token ability it needs in \"x-required-abilities\". Errors are problem details (application/problem+json). A guide for AI agents is at https://player.schule/api/v1/guide."},"servers":[{"url":"https://player.schule/api/v1"}],"security":[{"bearerAuth":[]}],"tags":[{"name":"Account","description":"The person the token acts for."},{"name":"Videos","description":"Your own videos of every type (uploaded, YouTube, Vimeo, animation)."},{"name":"Docs","description":"Descriptions of this API, no token needed."}],"paths":{"/openapi.json":{"get":{"tags":["Docs"],"operationId":"getOpenApi","summary":"This OpenAPI description","security":[],"responses":{"200":{"description":"OpenAPI 3.1 document","content":{"application/json":{"schema":{"type":"object"}}}}}}},"/guide":{"get":{"tags":["Docs"],"operationId":"getGuide","summary":"Guide for AI agents (Markdown)","security":[],"responses":{"200":{"description":"The guide","content":{"text/markdown":{"schema":{"type":"string"}}}}}}},"/me":{"get":{"tags":["Account"],"operationId":"getMe","summary":"Who the token acts for and what it may do","description":"Needs no ability. Use it to check a token.","x-required-abilities":[],"responses":{"200":{"description":"The account and the token","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Me"}}}},"401":{"description":"No valid token","content":{"application/problem+json":{"schema":{"$ref":"#/components/schemas/Problem"}}}},"429":{"description":"Too many requests (see the Retry-After header)","content":{"application/problem+json":{"schema":{"$ref":"#/components/schemas/Problem"}}}}}}},"/videos":{"get":{"tags":["Videos"],"operationId":"listVideos","summary":"List your videos","description":"Token ability: videos:read.","x-required-abilities":["videos:read"],"parameters":[{"name":"type","in":"query","schema":{"type":"string","enum":["video","youtube","vimeo","animation"]}},{"name":"status","in":"query","schema":{"type":"string","enum":["ready","processing"]}},{"name":"q","in":"query","description":"Search in name and description.","schema":{"type":"string","maxLength":200}},{"name":"sort","in":"query","description":"Column, with \"-\" in front for descending. Default \"-created_at\".","schema":{"type":"string","enum":["created_at","updated_at","name","views","duration","-created_at","-updated_at","-name","-views","-duration"]}},{"name":"page","in":"query","schema":{"type":"integer","minimum":1,"default":1}},{"name":"per_page","in":"query","schema":{"type":"integer","minimum":1,"maximum":100,"default":25}}],"responses":{"200":{"description":"A page of videos","content":{"application/json":{"schema":{"$ref":"#/components/schemas/VideoPage"}}}},"401":{"description":"No valid token","content":{"application/problem+json":{"schema":{"$ref":"#/components/schemas/Problem"}}}},"403":{"description":"The token lacks the ability","content":{"application/problem+json":{"schema":{"$ref":"#/components/schemas/Problem"}}}},"422":{"description":"Invalid input","content":{"application/problem+json":{"schema":{"$ref":"#/components/schemas/ValidationProblem"}}}},"429":{"description":"Too many requests (see the Retry-After header)","content":{"application/problem+json":{"schema":{"$ref":"#/components/schemas/Problem"}}}}}},"post":{"tags":["Videos"],"operationId":"createVideo","summary":"Upload a file or add a link","description":"Either multipart/form-data with \"file\" (any format ffmpeg reads, at most 512 MB; converted to MP4 1080p and 720p) or JSON with \"url\" (YouTube and Vimeo links are embedded, every other link is downloaded and converted). Limited to 10 requests per minute. Token ability: videos:write.","x-required-abilities":["videos:write"],"requestBody":{"required":true,"content":{"multipart/form-data":{"schema":{"type":"object","required":["file"],"properties":{"file":{"type":"string","contentMediaType":"application/octet-stream"},"name":{"type":"string","maxLength":250}}}},"application/json":{"schema":{"type":"object","required":["url"],"properties":{"url":{"type":"string","format":"uri","maxLength":2048},"name":{"type":"string","maxLength":250,"description":"Only used for a new YouTube or Vimeo entry."}}}}}},"responses":{"201":{"description":"The new video (an upload is \"processing\" until its files are made)","content":{"application/json":{"schema":{"$ref":"#/components/schemas/VideoEnvelope"}}}},"200":{"description":"The YouTube or Vimeo video was already in your list","content":{"application/json":{"schema":{"$ref":"#/components/schemas/VideoEnvelope"}}}},"202":{"description":"Any other link: the import has started, the video appears in the list when it is ready","content":{"application/json":{"schema":{"$ref":"#/components/schemas/ImportStarted"}}}},"413":{"description":"The file is larger than the server accepts","content":{"application/problem+json":{"schema":{"$ref":"#/components/schemas/Problem"}}}},"401":{"description":"No valid token","content":{"application/problem+json":{"schema":{"$ref":"#/components/schemas/Problem"}}}},"403":{"description":"The token lacks the ability","content":{"application/problem+json":{"schema":{"$ref":"#/components/schemas/Problem"}}}},"422":{"description":"Invalid input","content":{"application/problem+json":{"schema":{"$ref":"#/components/schemas/ValidationProblem"}}}},"429":{"description":"Too many requests (see the Retry-After header)","content":{"application/problem+json":{"schema":{"$ref":"#/components/schemas/Problem"}}}}}}},"/videos/{id}":{"get":{"tags":["Videos"],"operationId":"getVideo","summary":"Get one video","description":"Token ability: videos:read.","x-required-abilities":["videos:read"],"parameters":[{"name":"id","in":"path","required":true,"description":"The id of the video (a UUID).","schema":{"type":"string","format":"uuid"}}],"responses":{"200":{"description":"The video","content":{"application/json":{"schema":{"$ref":"#/components/schemas/VideoEnvelope"}}}},"401":{"description":"No valid token","content":{"application/problem+json":{"schema":{"$ref":"#/components/schemas/Problem"}}}},"403":{"description":"The token lacks the ability","content":{"application/problem+json":{"schema":{"$ref":"#/components/schemas/Problem"}}}},"404":{"description":"No video of yours has this id","content":{"application/problem+json":{"schema":{"$ref":"#/components/schemas/Problem"}}}},"422":{"description":"Invalid input","content":{"application/problem+json":{"schema":{"$ref":"#/components/schemas/ValidationProblem"}}}},"429":{"description":"Too many requests (see the Retry-After header)","content":{"application/problem+json":{"schema":{"$ref":"#/components/schemas/Problem"}}}}}},"patch":{"tags":["Videos"],"operationId":"updateVideo","summary":"Change name, description or password","description":"Only the keys you send are changed. \"password\" as a string protects an uploaded video or animation (viewers must enter it), null removes the protection. YouTube and Vimeo videos cannot be protected. Token ability: videos:write.","x-required-abilities":["videos:write"],"parameters":[{"name":"id","in":"path","required":true,"description":"The id of the video (a UUID).","schema":{"type":"string","format":"uuid"}}],"requestBody":{"required":true,"content":{"application/json":{"schema":{"type":"object","properties":{"name":{"type":"string","minLength":1,"maxLength":250},"description":{"type":["string","null"],"maxLength":5000},"password":{"type":["string","null"],"minLength":4,"maxLength":255}}}}}},"responses":{"200":{"description":"The changed video","content":{"application/json":{"schema":{"$ref":"#/components/schemas/VideoEnvelope"}}}},"401":{"description":"No valid token","content":{"application/problem+json":{"schema":{"$ref":"#/components/schemas/Problem"}}}},"403":{"description":"The token lacks the ability","content":{"application/problem+json":{"schema":{"$ref":"#/components/schemas/Problem"}}}},"404":{"description":"No video of yours has this id","content":{"application/problem+json":{"schema":{"$ref":"#/components/schemas/Problem"}}}},"422":{"description":"Invalid input","content":{"application/problem+json":{"schema":{"$ref":"#/components/schemas/ValidationProblem"}}}},"429":{"description":"Too many requests (see the Retry-After header)","content":{"application/problem+json":{"schema":{"$ref":"#/components/schemas/Problem"}}}}}},"delete":{"tags":["Videos"],"operationId":"deleteVideo","summary":"Delete a video and all of its files","description":"Token ability: videos:delete.","x-required-abilities":["videos:delete"],"parameters":[{"name":"id","in":"path","required":true,"description":"The id of the video (a UUID).","schema":{"type":"string","format":"uuid"}}],"responses":{"204":{"description":"Deleted"},"401":{"description":"No valid token","content":{"application/problem+json":{"schema":{"$ref":"#/components/schemas/Problem"}}}},"403":{"description":"The token lacks the ability","content":{"application/problem+json":{"schema":{"$ref":"#/components/schemas/Problem"}}}},"404":{"description":"No video of yours has this id","content":{"application/problem+json":{"schema":{"$ref":"#/components/schemas/Problem"}}}},"422":{"description":"Invalid input","content":{"application/problem+json":{"schema":{"$ref":"#/components/schemas/ValidationProblem"}}}},"429":{"description":"Too many requests (see the Retry-After header)","content":{"application/problem+json":{"schema":{"$ref":"#/components/schemas/Problem"}}}}}}},"/videos/{id}/reprocess":{"post":{"tags":["Videos"],"operationId":"reprocessVideo","summary":"Make the files of an uploaded video again","description":"Only for uploaded videos (type \"video\"). With \"import\": true an imported video is downloaded again from its source. Token ability: videos:write.","x-required-abilities":["videos:write"],"parameters":[{"name":"id","in":"path","required":true,"description":"The id of the video (a UUID).","schema":{"type":"string","format":"uuid"}}],"requestBody":{"required":false,"content":{"application/json":{"schema":{"type":"object","properties":{"import":{"type":"boolean","default":false}}}}}},"responses":{"202":{"description":"Processing has started","content":{"application/json":{"schema":{"$ref":"#/components/schemas/VideoEnvelope"}}}},"401":{"description":"No valid token","content":{"application/problem+json":{"schema":{"$ref":"#/components/schemas/Problem"}}}},"403":{"description":"The token lacks the ability","content":{"application/problem+json":{"schema":{"$ref":"#/components/schemas/Problem"}}}},"404":{"description":"No video of yours has this id","content":{"application/problem+json":{"schema":{"$ref":"#/components/schemas/Problem"}}}},"422":{"description":"Invalid input","content":{"application/problem+json":{"schema":{"$ref":"#/components/schemas/ValidationProblem"}}}},"429":{"description":"Too many requests (see the Retry-After header)","content":{"application/problem+json":{"schema":{"$ref":"#/components/schemas/Problem"}}}}}}},"/animations":{"post":{"tags":["Animations"],"operationId":"createAnimation","summary":"Create an animation","description":"JSON with \"name\" and an optional \"html\" composition, or multipart with an \"archive\" ZIP. Token ability: animations:write.","x-required-abilities":["animations:write"],"requestBody":{"required":true,"content":{"application/json":{"schema":{"type":"object","required":["name"],"properties":{"name":{"type":"string","maxLength":250},"html":{"type":"string","maxLength":400000}}}},"multipart/form-data":{"schema":{"type":"object","required":["archive"],"properties":{"archive":{"type":"string","contentMediaType":"application/zip"},"name":{"type":"string"}}}}}},"responses":{"201":{"description":"The new animation","content":{"application/json":{"schema":{"type":"object"}}}},"401":{"description":"No valid token","content":{"application/problem+json":{"schema":{"$ref":"#/components/schemas/Problem"}}}},"403":{"description":"The token lacks the ability","content":{"application/problem+json":{"schema":{"$ref":"#/components/schemas/Problem"}}}},"422":{"description":"Invalid input","content":{"application/problem+json":{"schema":{"$ref":"#/components/schemas/ValidationProblem"}}}},"429":{"description":"Too many requests (see the Retry-After header)","content":{"application/problem+json":{"schema":{"$ref":"#/components/schemas/Problem"}}}}}}},"/animations/{id}":{"get":{"tags":["Animations"],"operationId":"getAnimation","summary":"An animation with its composition, files and checks","description":"Token ability: animations:write, videos:read.","x-required-abilities":["animations:write","videos:read"],"parameters":[{"name":"id","in":"path","required":true,"schema":{"type":"string","format":"uuid"},"description":"The animation (a video of type animation)."}],"responses":{"200":{"description":"The animation","content":{"application/json":{"schema":{"type":"object"}}}},"401":{"description":"No valid token","content":{"application/problem+json":{"schema":{"$ref":"#/components/schemas/Problem"}}}},"403":{"description":"The token lacks the ability","content":{"application/problem+json":{"schema":{"$ref":"#/components/schemas/Problem"}}}},"404":{"description":"No video of yours has this id","content":{"application/problem+json":{"schema":{"$ref":"#/components/schemas/Problem"}}}},"422":{"description":"Invalid input","content":{"application/problem+json":{"schema":{"$ref":"#/components/schemas/ValidationProblem"}}}},"429":{"description":"Too many requests (see the Retry-After header)","content":{"application/problem+json":{"schema":{"$ref":"#/components/schemas/Problem"}}}}}}},"/animations/{id}/composition":{"put":{"tags":["Animations"],"operationId":"replaceAnimationComposition","summary":"Replace the whole composition","description":"Token ability: animations:write.","x-required-abilities":["animations:write"],"parameters":[{"name":"id","in":"path","required":true,"schema":{"type":"string","format":"uuid"},"description":"The animation (a video of type animation)."}],"requestBody":{"required":true,"content":{"application/json":{"schema":{"type":"object","required":["html"],"properties":{"html":{"type":"string","maxLength":400000,"description":"The complete composition."}}}}}},"responses":{"200":{"description":"The animation","content":{"application/json":{"schema":{"type":"object"}}}},"401":{"description":"No valid token","content":{"application/problem+json":{"schema":{"$ref":"#/components/schemas/Problem"}}}},"403":{"description":"The token lacks the ability","content":{"application/problem+json":{"schema":{"$ref":"#/components/schemas/Problem"}}}},"404":{"description":"No video of yours has this id","content":{"application/problem+json":{"schema":{"$ref":"#/components/schemas/Problem"}}}},"422":{"description":"Invalid input","content":{"application/problem+json":{"schema":{"$ref":"#/components/schemas/ValidationProblem"}}}},"429":{"description":"Too many requests (see the Retry-After header)","content":{"application/problem+json":{"schema":{"$ref":"#/components/schemas/Problem"}}}}}},"patch":{"tags":["Animations"],"operationId":"editAnimationComposition","summary":"Exact replacements, all or none","description":"Every old_str must occur exactly once in the composition. Token ability: animations:write.","x-required-abilities":["animations:write"],"parameters":[{"name":"id","in":"path","required":true,"schema":{"type":"string","format":"uuid"},"description":"The animation (a video of type animation)."}],"requestBody":{"required":true,"content":{"application/json":{"schema":{"type":"object","required":["edits"],"properties":{"edits":{"type":"array","maxItems":50,"items":{"type":"object","required":["old_str","new_str"],"properties":{"old_str":{"type":"string"},"new_str":{"type":"string"}}}}}}}}},"responses":{"200":{"description":"The animation","content":{"application/json":{"schema":{"type":"object"}}}},"401":{"description":"No valid token","content":{"application/problem+json":{"schema":{"$ref":"#/components/schemas/Problem"}}}},"403":{"description":"The token lacks the ability","content":{"application/problem+json":{"schema":{"$ref":"#/components/schemas/Problem"}}}},"404":{"description":"No video of yours has this id","content":{"application/problem+json":{"schema":{"$ref":"#/components/schemas/Problem"}}}},"422":{"description":"Invalid input","content":{"application/problem+json":{"schema":{"$ref":"#/components/schemas/ValidationProblem"}}}},"429":{"description":"Too many requests (see the Retry-After header)","content":{"application/problem+json":{"schema":{"$ref":"#/components/schemas/Problem"}}}}}}},"/animations/{id}/assets/{name}":{"put":{"tags":["Animations"],"operationId":"putAnimationAsset","summary":"Add or replace a file (raw body)","description":"Token ability: animations:write.","x-required-abilities":["animations:write"],"parameters":[{"name":"id","in":"path","required":true,"schema":{"type":"string","format":"uuid"},"description":"The animation (a video of type animation)."},{"name":"name","in":"path","required":true,"schema":{"type":"string","pattern":"^[a-z0-9][a-z0-9._-]{0,99}$"},"description":"File name below assets/."}],"requestBody":{"required":true,"content":{"application/octet-stream":{"schema":{"type":"string","contentMediaType":"application/octet-stream"}}}},"responses":{"201":{"description":"The file and the animation","content":{"application/json":{"schema":{"type":"object"}}}},"401":{"description":"No valid token","content":{"application/problem+json":{"schema":{"$ref":"#/components/schemas/Problem"}}}},"403":{"description":"The token lacks the ability","content":{"application/problem+json":{"schema":{"$ref":"#/components/schemas/Problem"}}}},"404":{"description":"No video of yours has this id","content":{"application/problem+json":{"schema":{"$ref":"#/components/schemas/Problem"}}}},"422":{"description":"Invalid input","content":{"application/problem+json":{"schema":{"$ref":"#/components/schemas/ValidationProblem"}}}},"429":{"description":"Too many requests (see the Retry-After header)","content":{"application/problem+json":{"schema":{"$ref":"#/components/schemas/Problem"}}}}}},"post":{"tags":["Animations"],"operationId":"postAnimationAsset","summary":"Add or replace a file (multipart field \"file\")","description":"Token ability: animations:write.","x-required-abilities":["animations:write"],"parameters":[{"name":"id","in":"path","required":true,"schema":{"type":"string","format":"uuid"},"description":"The animation (a video of type animation)."},{"name":"name","in":"path","required":true,"schema":{"type":"string","pattern":"^[a-z0-9][a-z0-9._-]{0,99}$"},"description":"File name below assets/."}],"requestBody":{"required":true,"content":{"multipart/form-data":{"schema":{"type":"object","required":["file"],"properties":{"file":{"type":"string","contentMediaType":"application/octet-stream"}}}}}},"responses":{"201":{"description":"The file and the animation","content":{"application/json":{"schema":{"type":"object"}}}},"401":{"description":"No valid token","content":{"application/problem+json":{"schema":{"$ref":"#/components/schemas/Problem"}}}},"403":{"description":"The token lacks the ability","content":{"application/problem+json":{"schema":{"$ref":"#/components/schemas/Problem"}}}},"404":{"description":"No video of yours has this id","content":{"application/problem+json":{"schema":{"$ref":"#/components/schemas/Problem"}}}},"422":{"description":"Invalid input","content":{"application/problem+json":{"schema":{"$ref":"#/components/schemas/ValidationProblem"}}}},"429":{"description":"Too many requests (see the Retry-After header)","content":{"application/problem+json":{"schema":{"$ref":"#/components/schemas/Problem"}}}}}},"delete":{"tags":["Animations"],"operationId":"deleteAnimationAsset","summary":"Remove a file for good","description":"Token ability: animations:write.","x-required-abilities":["animations:write"],"parameters":[{"name":"id","in":"path","required":true,"schema":{"type":"string","format":"uuid"},"description":"The animation (a video of type animation)."},{"name":"name","in":"path","required":true,"schema":{"type":"string","pattern":"^[a-z0-9][a-z0-9._-]{0,99}$"},"description":"File name below assets/."}],"responses":{"200":{"description":"The animation","content":{"application/json":{"schema":{"type":"object"}}}},"401":{"description":"No valid token","content":{"application/problem+json":{"schema":{"$ref":"#/components/schemas/Problem"}}}},"403":{"description":"The token lacks the ability","content":{"application/problem+json":{"schema":{"$ref":"#/components/schemas/Problem"}}}},"404":{"description":"No video of yours has this id","content":{"application/problem+json":{"schema":{"$ref":"#/components/schemas/Problem"}}}},"422":{"description":"Invalid input","content":{"application/problem+json":{"schema":{"$ref":"#/components/schemas/ValidationProblem"}}}},"429":{"description":"Too many requests (see the Retry-After header)","content":{"application/problem+json":{"schema":{"$ref":"#/components/schemas/Problem"}}}}}}},"/animations/{id}/unused-assets":{"delete":{"tags":["Animations"],"operationId":"deleteUnusedAnimationAssets","summary":"Delete every file the composition does not use","description":"Token ability: animations:write.","x-required-abilities":["animations:write"],"parameters":[{"name":"id","in":"path","required":true,"schema":{"type":"string","format":"uuid"},"description":"The animation (a video of type animation)."}],"responses":{"200":{"description":"The removed files and the animation","content":{"application/json":{"schema":{"type":"object"}}}},"401":{"description":"No valid token","content":{"application/problem+json":{"schema":{"$ref":"#/components/schemas/Problem"}}}},"403":{"description":"The token lacks the ability","content":{"application/problem+json":{"schema":{"$ref":"#/components/schemas/Problem"}}}},"404":{"description":"No video of yours has this id","content":{"application/problem+json":{"schema":{"$ref":"#/components/schemas/Problem"}}}},"422":{"description":"Invalid input","content":{"application/problem+json":{"schema":{"$ref":"#/components/schemas/ValidationProblem"}}}},"429":{"description":"Too many requests (see the Retry-After header)","content":{"application/problem+json":{"schema":{"$ref":"#/components/schemas/Problem"}}}}}}},"/animations/{id}/check":{"get":{"tags":["Animations"],"operationId":"checkAnimation","summary":"What keeps the animation from playing","description":"Token ability: animations:write, videos:read.","x-required-abilities":["animations:write","videos:read"],"parameters":[{"name":"id","in":"path","required":true,"schema":{"type":"string","format":"uuid"},"description":"The animation (a video of type animation)."}],"responses":{"200":{"description":"A list of sentences","content":{"application/json":{"schema":{"type":"object"}}}},"401":{"description":"No valid token","content":{"application/problem+json":{"schema":{"$ref":"#/components/schemas/Problem"}}}},"403":{"description":"The token lacks the ability","content":{"application/problem+json":{"schema":{"$ref":"#/components/schemas/Problem"}}}},"404":{"description":"No video of yours has this id","content":{"application/problem+json":{"schema":{"$ref":"#/components/schemas/Problem"}}}},"422":{"description":"Invalid input","content":{"application/problem+json":{"schema":{"$ref":"#/components/schemas/ValidationProblem"}}}},"429":{"description":"Too many requests (see the Retry-After header)","content":{"application/problem+json":{"schema":{"$ref":"#/components/schemas/Problem"}}}}}}}},"components":{"securitySchemes":{"bearerAuth":{"type":"http","scheme":"bearer","description":"A personal API token. Abilities: videos:read, videos:write, videos:delete, animations:write (\"*\" grants all)."}},"schemas":{"Video":{"type":"object","properties":{"id":{"type":"string","format":"uuid"},"type":{"type":"string","enum":["video","youtube","vimeo","animation"]},"name":{"type":"string"},"description":{"type":["string","null"]},"status":{"type":"string","enum":["ready","processing"]},"progress":{"type":"integer","minimum":0,"maximum":100},"duration":{"type":["integer","null"],"description":"Seconds"},"views":{"type":"integer"},"protected":{"type":"boolean","description":"Viewers must enter a password."},"source_url":{"type":["string","null"],"format":"uri"},"play_url":{"type":"string","format":"uri","description":"Public page of the video, to share."},"embed_url":{"type":"string","format":"uri","description":"For an iframe."},"embed_code":{"type":"string","description":"HTML to paste into another page."},"renditions":{"type":"array","items":{"type":"integer"},"description":"Heights of the MP4 files of an uploaded video, e.g. [1080, 720]."},"created_at":{"type":"string","format":"date-time"},"updated_at":{"type":"string","format":"date-time"}}},"VideoEnvelope":{"type":"object","properties":{"data":{"$ref":"#/components/schemas/Video"}}},"VideoPage":{"type":"object","properties":{"data":{"type":"array","items":{"$ref":"#/components/schemas/Video"}},"links":{"type":"object","properties":{"first":{"type":["string","null"]},"last":{"type":["string","null"]},"prev":{"type":["string","null"]},"next":{"type":["string","null"]}}},"meta":{"type":"object","properties":{"current_page":{"type":"integer"},"last_page":{"type":"integer"},"per_page":{"type":"integer"},"total":{"type":"integer"}}}}},"ImportStarted":{"type":"object","properties":{"data":{"type":"null"},"imported":{"type":"boolean","const":true},"message":{"type":"string"}}},"Me":{"type":"object","properties":{"data":{"type":"object","properties":{"name":{"type":"string"},"email":{"type":"string","format":"email"},"videos":{"type":"integer"},"token":{"type":"object","properties":{"name":{"type":"string"},"abilities":{"type":"array","items":{"type":"string"}},"last_used_at":{"type":["string","null"],"format":"date-time"},"expires_at":{"type":["string","null"],"format":"date-time"}}}}}}},"Problem":{"type":"object","description":"RFC 9457 problem details.","required":["type","title","status","detail"],"properties":{"type":{"type":"string"},"title":{"type":"string"},"status":{"type":"integer"},"detail":{"type":"string"}}},"ValidationProblem":{"allOf":[{"$ref":"#/components/schemas/Problem"},{"type":"object","properties":{"errors":{"type":"object","additionalProperties":{"type":"array","items":{"type":"string"}}}}}]}}}}