{"components":{"securitySchemes":{"ApiKeyAuth":{"in":"header","name":"X-API-Key","type":"apiKey"}}},"info":{"title":"Dyalog Content Management System","version":"4.1.3"},"openapi":"3.0.3","paths":{"/":{"get":{"operationId":"getDocs","responses":{"200":{"description":"HTML page hosting the Swagger UI"}},"security":[],"summary":"Swagger UI documentation page","tags":["Web UI"]}},"/admin/profile":{"delete":{"operationId":"clearProfile","responses":{"204":{"description":"Profile data cleared. No response body."},"401":{"content":{"application/json":{"schema":{"properties":{"error":{"description":"Machine-readable error code","type":"string"},"message":{"description":"Human-readable description","type":"string"}},"required":["error","message"],"type":"object"}}},"description":"Missing or invalid API key.","example":{"error":"unauthorized","message":"Missing or invalid X-API-Key header"}}},"summary":"Clear profile data","tags":["Admin"]},"get":{"operationId":"getProfile","responses":{"200":{"content":{"application/json":{"schema":{"items":{"properties":{"exclusive_time":{"description":"Milliseconds in this function, excluding called functions.","format":"double","type":"number"},"fn_name":{"type":"string"},"inclusive_time":{"description":"Milliseconds in this function, including called functions.","format":"double","type":"number"},"line_no":{"type":"integer"},"n_calls":{"type":"integer"}},"type":"object"},"type":"array"}}},"description":"A list of objects, one per profiled line of code. Empty array if profiling has never been started or data has been cleared."},"401":{"content":{"application/json":{"schema":{"properties":{"error":{"description":"Machine-readable error code","type":"string"},"message":{"description":"Human-readable description","type":"string"}},"required":["error","message"],"type":"object"}}},"description":"Missing or invalid API key.","example":{"error":"unauthorized","message":"Missing or invalid X-API-Key header"}}},"summary":"Get profile data collected by \u2395PROFILE","tags":["Admin"]}},"/admin/profiling":{"get":{"operationId":"getProfilingStatus","responses":{"200":{"content":{"application/json":{"schema":{"properties":{"last_cleared":{"description":"Timestamp of the most recent DELETE /admin/profile; null if never cleared.","nullable":true,"type":"string"},"on":{"description":"Whether \u2395PROFILE is currently collecting.","type":"boolean"},"since":{"description":"Timestamp of the most recent on/off transition; null if never started.","nullable":true,"type":"string"}},"type":"object"}}},"description":"Current profiling status"},"401":{"content":{"application/json":{"schema":{"properties":{"error":{"description":"Machine-readable error code","type":"string"},"message":{"description":"Human-readable description","type":"string"}},"required":["error","message"],"type":"object"}}},"description":"Missing or invalid API key.","example":{"error":"unauthorized","message":"Missing or invalid X-API-Key header"}}},"summary":"Get profiling status","tags":["Admin"]},"put":{"operationId":"setProfilingStatus","parameters":[{"description":"Set to true to enable profiling, false to disable.","in":"query","name":"on","required":true,"schema":{"enum":["true","false"],"type":"string"}}],"responses":{"204":{"description":"State set (or already in the requested state). No response body."},"400":{"content":{"application/json":{"schema":{"properties":{"error":{"description":"Machine-readable error code","type":"string"},"message":{"description":"Human-readable description","type":"string"}},"required":["error","message"],"type":"object"}}},"description":"Missing on query parameter, or value is not true or false."},"401":{"content":{"application/json":{"schema":{"properties":{"error":{"description":"Machine-readable error code","type":"string"},"message":{"description":"Human-readable description","type":"string"}},"required":["error","message"],"type":"object"}}},"description":"Missing or invalid API key.","example":{"error":"unauthorized","message":"Missing or invalid X-API-Key header"}}},"summary":"Enable or disable profiling","tags":["Admin"]}},"/admin/refresh":{"post":{"description":"Fetches external API data, then rebuilds the cached joined views served by the public read endpoints.","operationId":"refreshData","responses":{"200":{"description":"Successful response"}},"summary":"Refresh cached API data","tags":["Admin"]}},"/admin/wp-push":{"post":{"operationId":"triggerWPPush","responses":{"200":{"content":{"application/json":{"schema":{"items":{"properties":{"action":{"enum":["create","delete","update"],"type":"string"},"post_id":{"type":"integer"},"success":{"type":"boolean"}},"type":"object"},"type":"array"}}},"description":"Push processed. Returns the result of each WordPress post action."},"401":{"content":{"application/json":{"schema":{"properties":{"error":{"description":"Machine-readable error code","type":"string"},"message":{"description":"Human-readable description","type":"string"}},"required":["error","message"],"type":"object"}}},"description":"Missing or invalid API key.","example":{"error":"unauthorized","message":"Missing or invalid X-API-Key header"}}},"summary":"Trigger a push of WordPress Team Dyalog video listings","tags":["Admin"]}},"/crud":{"get":{"operationId":"listTables","responses":{"200":{"content":{"application/json":{"schema":{"properties":{"tables":{"items":{"type":"string"},"type":"array"}},"required":["tables"],"type":"object"}}},"description":"Names of tables exposed under /crud/."},"401":{"content":{"application/json":{"schema":{"properties":{"error":{"description":"Machine-readable error code","type":"string"},"message":{"description":"Human-readable description","type":"string"}},"required":["error","message"],"type":"object"}}},"description":"Missing or invalid API key.","example":{"error":"unauthorized","message":"Missing or invalid X-API-Key header"}}},"summary":"List available tables","tags":["Table CRUD"]}},"/crud/{table}":{"get":{"operationId":"listRows","parameters":[{"in":"path","name":"table","required":true,"schema":{"type":"string"}},{"description":"Page number.","in":"query","name":"page","required":false,"schema":{"default":1,"minimum":1,"type":"integer"}},{"description":"Number of results per page.","in":"query","name":"per_page","required":false,"schema":{"default":20,"minimum":1,"type":"integer"}}],"responses":{"200":{"content":{"application/json":{"schema":{"allOf":[{"description":"Pagination metadata wrapper. The \"data\" field varies by endpoint.","properties":{"current_page":{"type":"integer"},"first_page":{"type":"integer"},"first_page_url":{"format":"uri","type":"string"},"from":{"description":"Ordinal of the first result on this page","type":"integer"},"last_page":{"type":"integer"},"last_page_url":{"format":"uri","type":"string"},"links":{"items":{"description":"Describes a numbered, previous, or next button for paginated results.","properties":{"active":{"type":"boolean"},"label":{"type":"string"},"url":{"format":"uri","nullable":true,"type":"string"}},"type":"object"},"type":"array"},"next_page_url":{"format":"uri","nullable":true,"type":"string"},"path":{"description":"The URL for this endpoint","type":"string"},"per_page":{"type":"integer"},"prev_page_url":{"format":"uri","nullable":true,"type":"string"},"to":{"description":"Ordinal of the last result on this page","type":"integer"},"total":{"description":"Total count of results for this query, across all pages","type":"integer"}},"type":"object"},{"properties":{"data":{"items":{"additionalProperties":true,"description":"A table row. Fields beyond \"id\" depend on the table.","properties":{"id":{"type":"integer"}},"type":"object"},"type":"array"}},"type":"object"}]}}},"description":"Paginated list of table rows."},"401":{"content":{"application/json":{"schema":{"properties":{"error":{"description":"Machine-readable error code","type":"string"},"message":{"description":"Human-readable description","type":"string"}},"required":["error","message"],"type":"object"}}},"description":"Missing or invalid API key.","example":{"error":"unauthorized","message":"Missing or invalid X-API-Key header"}},"404":{"content":{"application/json":{"schema":{"properties":{"error":{"description":"Machine-readable error code","type":"string"},"message":{"description":"Human-readable description","type":"string"}},"required":["error","message"],"type":"object"}}},"description":"Resource not found"}},"summary":"List rows from table","tags":["Table CRUD"]},"post":{"body":{"content":{"application/json":{"schema":{"items":{"additionalProperties":true,"description":"A table row. Fields beyond \"id\" depend on the table.","properties":{"id":{"type":"integer"}},"type":"object"},"type":"array"}}},"required":true},"operationId":"createRows","parameters":[{"in":"path","name":"table","required":true,"schema":{"type":"string"}}],"responses":{"201":{"content":{"application/json":{"schema":{"items":{"additionalProperties":true,"description":"A table row. Fields beyond \"id\" depend on the table.","properties":{"id":{"type":"integer"}},"type":"object"},"type":"array"}}},"description":"Created. Returns affected records."},"400":{"content":{"application/json":{"schema":{"properties":{"error":{"description":"Machine-readable error code","type":"string"},"message":{"description":"Human-readable description","type":"string"}},"required":["error","message"],"type":"object"}}},"description":"Invalid payload. Request body must be a JSON array of objects."},"401":{"content":{"application/json":{"schema":{"properties":{"error":{"description":"Machine-readable error code","type":"string"},"message":{"description":"Human-readable description","type":"string"}},"required":["error","message"],"type":"object"}}},"description":"Missing or invalid API key.","example":{"error":"unauthorized","message":"Missing or invalid X-API-Key header"}},"404":{"content":{"application/json":{"schema":{"properties":{"error":{"description":"Machine-readable error code","type":"string"},"message":{"description":"Human-readable description","type":"string"}},"required":["error","message"],"type":"object"}}},"description":"Resource not found"}},"summary":"Create rows","tags":["Table CRUD"]}},"/crud/{table}/{id}":{"delete":{"operationId":"deleteRow","parameters":[{"in":"path","name":"table","required":true,"schema":{"type":"string"}},{"in":"path","name":"id","required":true,"schema":{"type":"string"}}],"responses":{"204":{"description":"Deleted successfully. No content returned."},"400":{"content":{"application/json":{"schema":{"properties":{"error":{"description":"Machine-readable error code","type":"string"},"message":{"description":"Human-readable description","type":"string"}},"required":["error","message"],"type":"object"}}},"description":"Bad request, e.g. invalid ID parameter."},"401":{"content":{"application/json":{"schema":{"properties":{"error":{"description":"Machine-readable error code","type":"string"},"message":{"description":"Human-readable description","type":"string"}},"required":["error","message"],"type":"object"}}},"description":"Missing or invalid API key.","example":{"error":"unauthorized","message":"Missing or invalid X-API-Key header"}},"404":{"content":{"application/json":{"schema":{"properties":{"error":{"description":"Machine-readable error code","type":"string"},"message":{"description":"Human-readable description","type":"string"}},"required":["error","message"],"type":"object"}}},"description":"Resource not found"},"409":{"content":{"application/json":{"schema":{"properties":{"error":{"description":"Machine-readable error code","type":"string"},"message":{"description":"Human-readable description","type":"string"}},"required":["error","message"],"type":"object"}}},"description":"Delete blocked by foreign key constraint."}},"summary":"Delete a row","tags":["Table CRUD"]},"get":{"operationId":"getRow","parameters":[{"in":"path","name":"table","required":true,"schema":{"type":"string"}},{"in":"path","name":"id","required":true,"schema":{"type":"string"}}],"responses":{"200":{"content":{"application/json":{"schema":{"additionalProperties":true,"description":"A table row. Fields beyond \"id\" depend on the table.","properties":{"id":{"type":"integer"}},"type":"object"}}},"description":"A single table row."},"400":{"content":{"application/json":{"schema":{"properties":{"error":{"description":"Machine-readable error code","type":"string"},"message":{"description":"Human-readable description","type":"string"}},"required":["error","message"],"type":"object"}}},"description":"Bad request, e.g. invalid ID parameter."},"401":{"content":{"application/json":{"schema":{"properties":{"error":{"description":"Machine-readable error code","type":"string"},"message":{"description":"Human-readable description","type":"string"}},"required":["error","message"],"type":"object"}}},"description":"Missing or invalid API key.","example":{"error":"unauthorized","message":"Missing or invalid X-API-Key header"}},"404":{"content":{"application/json":{"schema":{"properties":{"error":{"description":"Machine-readable error code","type":"string"},"message":{"description":"Human-readable description","type":"string"}},"required":["error","message"],"type":"object"}}},"description":"Resource not found"}},"summary":"Get row by ID","tags":["Table CRUD"]},"patch":{"body":{"content":{"application/json":{"schema":{"additionalProperties":true,"description":"A table row. Fields beyond \"id\" depend on the table.","properties":{"id":{"type":"integer"}},"type":"object"}}}},"operationId":"updateRow","parameters":[{"in":"path","name":"table","required":true,"schema":{"type":"string"}},{"in":"path","name":"id","required":true,"schema":{"type":"string"}}],"responses":{"200":{"content":{"application/json":{"schema":{"additionalProperties":true,"description":"A table row. Fields beyond \"id\" depend on the table.","properties":{"id":{"type":"integer"}},"type":"object"}}},"description":"Updated. Returns the updated record."},"400":{"content":{"application/json":{"schema":{"properties":{"error":{"description":"Machine-readable error code","type":"string"},"message":{"description":"Human-readable description","type":"string"}},"required":["error","message"],"type":"object"}}},"description":"Invalid payload. Request body must be a JSON object."},"401":{"content":{"application/json":{"schema":{"properties":{"error":{"description":"Machine-readable error code","type":"string"},"message":{"description":"Human-readable description","type":"string"}},"required":["error","message"],"type":"object"}}},"description":"Missing or invalid API key.","example":{"error":"unauthorized","message":"Missing or invalid X-API-Key header"}},"404":{"content":{"application/json":{"schema":{"properties":{"error":{"description":"Machine-readable error code","type":"string"},"message":{"description":"Human-readable description","type":"string"}},"required":["error","message"],"type":"object"}}},"description":"Resource not found"}},"summary":"Update a row","tags":["Table CRUD"]}},"/events":{"get":{"operationId":"getEvents","parameters":[{"description":"Filter whether events have videos of presentations.","in":"query","name":"has_videos","required":false,"schema":{"type":"boolean"}}],"responses":{"200":{"content":{"application/json":{"schema":{"items":{"properties":{"end":{"description":"YYYY-MM-DD","format":"date","type":"string"},"has_videos":{"type":"boolean"},"id":{"type":"integer"},"start":{"description":"YYYY-MM-DD","format":"date","type":"string"},"title":{"type":"string"},"type":{"type":"string"},"url_slug":{"type":"string"}},"type":"object"},"type":"array"}}},"description":"List of events"}},"security":[],"summary":"List events","tags":["Events"]}},"/presenters":{"get":{"operationId":"getPresenters","responses":{"200":{"content":{"application/json":{"schema":{"properties":{"id":{"type":"integer"},"name":{"type":"string"}},"type":"object"}}},"description":"List of presenter names and IDs"}},"security":[],"summary":"List presenter names","tags":["Presenters"]}},"/schema/{table}":{"get":{"operationId":"getTableSchema","parameters":[{"in":"path","name":"table","required":true,"schema":{"type":"string"}}],"responses":{"200":{"content":{"application/json":{"schema":{"properties":{"columns":{"items":{"properties":{"json_type":{"enum":["string","integer"],"type":"string"},"length":{"type":"integer"},"name":{"type":"string"},"references":{"description":"Present iff the column is a foreign key. Absent otherwise.","properties":{"column":{"description":"Referenced column name (typically \"id\")","type":"string"},"table":{"description":"Referenced table name","type":"string"}},"required":["table","column"],"type":"object"},"sqapl_type":{"description":"SQAPL type annotation (e.g. <C100, <I, <D, <C19(S))","type":"string"}},"required":["name","sqapl_type","length","json_type"],"type":"object"},"type":"array"},"table":{"type":"string"}},"required":["table","columns"],"type":"object"}}},"description":"Column metadata for the table."},"401":{"content":{"application/json":{"schema":{"properties":{"error":{"description":"Machine-readable error code","type":"string"},"message":{"description":"Human-readable description","type":"string"}},"required":["error","message"],"type":"object"}}},"description":"Missing or invalid API key.","example":{"error":"unauthorized","message":"Missing or invalid X-API-Key header"}},"404":{"content":{"application/json":{"schema":{"properties":{"error":{"description":"Machine-readable error code","type":"string"},"message":{"description":"Human-readable description","type":"string"}},"required":["error","message"],"type":"object"}}},"description":"Resource not found"}},"summary":"Describe table columns","tags":["Table CRUD"]}},"/swagger/swagger-ui-bundle.js":{"get":{"operationId":"getSwaggerBundle","responses":{"200":{"description":"JavaScript"}},"security":[],"summary":"Swagger UI script bundle","tags":["Web UI"]}},"/swagger/swagger-ui.css":{"get":{"operationId":"getSwaggerCss","responses":{"200":{"description":"CSS"}},"security":[],"summary":"Swagger UI stylesheet","tags":["Web UI"]}},"/swagger/theme.css":{"get":{"operationId":"getThemeCss","responses":{"200":{"description":"CSS"}},"security":[],"summary":"Dyalog theme stylesheet","tags":["Web UI"]}},"/teapot":{"get":{"operationId":"Teapot","responses":{"418":"AM TEAPOT"}}},"/version":{"get":{"operationId":"getVersion","responses":{"200":{"content":{"application/json":{"schema":{"example":"4.0.0","type":"string"}}},"description":"Version string"}},"security":[],"summary":"Get API version","tags":["System"]}},"/videos":{"get":{"description":"Returns a paginated envelope of video results. All responses use the envelope format with data, pagination metadata, and navigation links.","operationId":"getVideos","parameters":[{"in":"query","name":"from","required":0,"schema":{"format":"date-time","type":"string"}},{"in":"query","name":"to","required":0,"schema":{"format":"date-time","type":"string"}},{"description":"Comma-separated list of presenter IDs, e.g. \"123,456\"","explode":false,"in":"query","name":"presenter","required":0,"schema":{"items":{"type":"integer"},"type":"array"}},{"description":"URL slug of event, e.g. \"dyalog-23\"","in":"query","name":"event","required":0,"schema":{"type":"string"}},{"description":"Full-text search across video titles, descriptions, presenter names, and event names","in":"query","name":"search","required":0,"schema":{"type":"string"}},{"description":"Sort order of results. Defaults to \"relevance\" when `search` is supplied, otherwise \"newest\".","in":"query","name":"sort","required":0,"schema":{"enum":["relevance","newest","oldest"],"type":"string"}},{"description":"Page number for paginated results.","in":"query","name":"page","required":0,"schema":{"default":1,"minimum":1,"type":"integer"}},{"description":"Number of results per page for paginated results. Defaults to 20.","in":"query","name":"per_page","required":0,"schema":{"default":20,"minimum":1,"type":"integer"}}],"responses":{"200":{"content":{"application/json":{"schema":{"properties":{"current_page":{"type":"integer"},"data":{"items":{"properties":{"category":{"type":"string"},"description":{"type":"string"},"event":{"type":"string"},"event_shortname":{"type":"string"},"presented_at":{"description":"Presentation date as YYYY-MM-DD hh:mm:ss","format":"date-time","type":"string"},"presenter":{"description":"Comma-separated list of presenter names","type":"string"},"presenter_id":{"description":"List of presenter IDs","items":{"type":"integer"},"type":"array"},"published_at":{"description":"YouTube publish date as YYYY-MM-DD hh:mm:ss","format":"date-time","type":"string"},"thumbnail":{"format":"uri","type":"string"},"title":{"type":"string"},"url":{"format":"uri","type":"string"},"youtube_id":{"type":"string"}},"type":"object"},"type":"array"},"first_page":{"description":"Page number of the first page of results (always 1)","type":"integer"},"first_page_url":{"format":"uri","type":"string"},"from":{"description":"Ordinal of the first result on this page","type":"integer"},"last_page":{"description":"Page number of the last page of results","type":"integer"},"last_page_url":{"format":"uri","type":"string"},"links":{"items":{"description":"Describes numbered, previous and next buttons for paginated results.","properties":{"active":{"type":"boolean"},"label":{"type":"string"},"url":{"format":"uri","nullable":true,"type":"string"}},"type":"object"},"type":"array"},"next_page_url":{"format":"uri","nullable":true,"type":"string"},"path":{"description":"URL for this endpoint","type":"string"},"per_page":{"type":"integer"},"prev_page_url":{"format":"uri","nullable":true,"type":"string"},"to":{"description":"Ordinal of the last result on this page","type":"integer"},"total":{"description":"Total count of results for this query, across all pages","type":"integer"}},"type":"object"}}},"description":"Paginated video results"},"404":{"content":{"application/json":{"schema":{"properties":{"error":{"description":"Machine-readable error code","type":"string"},"message":{"description":"Human-readable description","type":"string"}},"required":["error","message"],"type":"object"}}},"description":"No videos found matching the query parameters"},"422":{"content":{"application/json":{"schema":{"properties":{"error":{"description":"Machine-readable error code","type":"string"},"message":{"description":"Human-readable description","type":"string"}},"required":["error","message"],"type":"object"}}},"description":"Invalid query parameters"},"500":{"content":{"application/json":{"schema":{"properties":{"error":{"description":"Machine-readable error code","type":"string"},"message":{"description":"Human-readable description","type":"string"}},"required":["error","message"],"type":"object"}}},"description":"Internal server error"}},"security":[],"summary":"Search and list videos","tags":["Videos"]}},"/videos/{youtube_id}":{"get":{"operationId":"getVideo","parameters":[{"in":"path","name":"youtube_id","required":true,"schema":{"type":"string"}}],"responses":{"200":{"content":{"application/json":{"schema":{"properties":{"category":{"type":"string"},"description":{"type":"string"},"event":{"type":"string"},"event_shortname":{"type":"string"},"presented_at":{"description":"Presentation date as YYYY-MM-DD hh:mm:ss","format":"date-time","type":"string"},"presenter":{"description":"Comma-separated list of presenter names","type":"string"},"presenter_id":{"description":"List of presenter IDs","items":{"type":"integer"},"type":"array"},"published_at":{"description":"YouTube publish date as YYYY-MM-DD hh:mm:ss","format":"date-time","type":"string"},"thumbnail":{"format":"uri","type":"string"},"title":{"type":"string"},"url":{"format":"uri","type":"string"},"youtube_id":{"type":"string"}},"type":"object"}}},"description":"A single video object"},"404":{"content":{"application/json":{"schema":{"properties":{"error":{"description":"Machine-readable error code","type":"string"},"message":{"description":"Human-readable description","type":"string"}},"required":["error","message"],"type":"object"}}},"description":"Video not found"}},"security":[],"summary":"Get a single video","tags":["Videos"]}},"/videos/{youtube_id}/recommendations":{"get":{"operationId":"getVideoRecommendations","parameters":[{"in":"path","name":"youtube_id","required":true,"schema":{"type":"string"}},{"description":"11-character YouTube video ID","in":"path","name":"youtube_id","required":true,"schema":{"type":"string"}},{"description":"Number of recommendations to return","in":"query","name":"n","required":false,"schema":{"default":5,"minimum":1,"type":"integer"}}],"responses":{"200":{"content":{"application/json":{"schema":{"items":{"properties":{"category":{"type":"string"},"description":{"type":"string"},"event":{"type":"string"},"event_shortname":{"type":"string"},"presented_at":{"description":"Presentation date as YYYY-MM-DD hh:mm:ss","format":"date-time","type":"string"},"presenter":{"description":"Comma-separated list of presenter names","type":"string"},"presenter_id":{"description":"List of presenter IDs","items":{"type":"integer"},"type":"array"},"published_at":{"description":"YouTube publish date as YYYY-MM-DD hh:mm:ss","format":"date-time","type":"string"},"thumbnail":{"format":"uri","type":"string"},"title":{"type":"string"},"url":{"format":"uri","type":"string"},"youtube_id":{"type":"string"}},"type":"object"},"type":"array"}}},"description":"List of recommended videos"},"400":{"content":{"application/json":{"schema":{"properties":{"error":{"description":"Machine-readable error code","type":"string"},"message":{"description":"Human-readable description","type":"string"}},"required":["error","message"],"type":"object"}}},"description":"Bad request, e.g. invalid YouTube ID format"},"404":{"content":{"application/json":{"schema":{"properties":{"error":{"description":"Machine-readable error code","type":"string"},"message":{"description":"Human-readable description","type":"string"}},"required":["error","message"],"type":"object"}}},"description":"Video not found"}},"security":[],"summary":"Get video recommendations","tags":["Videos"]}}},"security":[{"ApiKeyAuth":[]}]}