{"components":{"schemas":{"ApiErrorResponse":{"properties":{"error":{"type":"string"}},"required":["error"],"type":"object"},"CampaignListResponse":{"properties":{"items":{"items":{"$ref":"#/components/schemas/CampaignResponse"},"type":"array"}},"required":["items"],"type":"object"},"CampaignResponse":{"properties":{"created_at":{"format":"date-time","type":"string"},"description":{"type":["string","null"]},"id":{"format":"uuid","type":"string"},"name":{"type":"string"},"slug":{"type":"string"}},"required":["id","slug","name","created_at"],"type":"object"},"CreateCampaignRequest":{"properties":{"description":{"type":["string","null"]},"name":{"type":"string"},"slug":{"type":"string"}},"required":["slug","name"],"type":"object"},"CreateLinkRequest":{"properties":{"campaign_id":{"format":"uuid","type":["string","null"]},"code":{"type":["string","null"]},"default_http_status":{"format":"int32","type":["integer","null"]},"expires_at":{"format":"date-time","type":["string","null"]},"target_url":{"type":"string"}},"required":["target_url"],"type":"object"},"HealthResponse":{"properties":{"status":{"type":"string"}},"required":["status"],"type":"object"},"LinkListResponse":{"properties":{"items":{"items":{"$ref":"#/components/schemas/LinkResponse"},"type":"array"}},"required":["items"],"type":"object"},"LinkResponse":{"properties":{"campaign_id":{"format":"uuid","type":["string","null"]},"code":{"type":"string"},"created_at":{"format":"date-time","type":"string"},"current_target":{"$ref":"#/components/schemas/LinkTargetResponse"},"default_http_status":{"format":"int32","type":"integer"},"expires_at":{"format":"date-time","type":["string","null"]},"id":{"format":"uuid","type":"string"},"is_active":{"type":"boolean"},"qr_url":{"type":"string"},"short_url":{"type":"string"},"updated_at":{"format":"date-time","type":"string"}},"required":["id","code","short_url","qr_url","is_active","default_http_status","created_at","updated_at","current_target"],"type":"object"},"LinkStatsBucketResponse":{"properties":{"bucket_start":{"format":"date-time","type":"string"},"human_redirects":{"format":"int64","type":"integer"},"total_redirects":{"format":"int64","type":"integer"}},"required":["bucket_start","total_redirects","human_redirects"],"type":"object"},"LinkStatsResponse":{"properties":{"bot_redirects":{"format":"int64","type":"integer"},"by_day":{"items":{"$ref":"#/components/schemas/LinkStatsBucketResponse"},"type":"array"},"human_redirects":{"format":"int64","type":"integer"},"preview_redirects":{"format":"int64","type":"integer"},"total_redirects":{"format":"int64","type":"integer"},"unique_visitors":{"format":"int64","type":"integer"}},"required":["total_redirects","human_redirects","preview_redirects","bot_redirects","unique_visitors","by_day"],"type":"object"},"LinkTargetResponse":{"properties":{"target_url":{"type":"string"},"version":{"format":"int32","type":"integer"}},"required":["version","target_url"],"type":"object"},"RetargetLinkRequest":{"properties":{"target_url":{"type":"string"}},"required":["target_url"],"type":"object"},"UpdateLinkRequest":{"properties":{"campaign_id":{"format":"uuid","type":["string","null"]},"clear_expires_at":{"type":"boolean"},"code":{"type":["string","null"]},"default_http_status":{"format":"int32","type":["integer","null"]},"expires_at":{"format":"date-time","type":["string","null"]},"is_active":{"type":["boolean","null"]}},"type":"object"}}},"info":{"description":"Redirect, QR, and admin API for Voices Against War short links.","license":{"identifier":"MIT","name":"MIT"},"title":"VAW Link Service API","version":"0.1.0"},"openapi":"3.1.0","paths":{"/api/campaigns":{"get":{"operationId":"list_campaigns","responses":{"200":{"content":{"application/json":{"schema":{"$ref":"#/components/schemas/CampaignListResponse"}}},"description":"List campaigns"},"401":{"content":{"application/json":{"schema":{"$ref":"#/components/schemas/ApiErrorResponse"}}},"description":"Unauthorized"}},"tags":["admin"]},"post":{"operationId":"create_campaign","requestBody":{"content":{"application/json":{"schema":{"$ref":"#/components/schemas/CreateCampaignRequest"}}},"required":true},"responses":{"200":{"content":{"application/json":{"schema":{"$ref":"#/components/schemas/CampaignResponse"}}},"description":"Campaign created"},"400":{"content":{"application/json":{"schema":{"$ref":"#/components/schemas/ApiErrorResponse"}}},"description":"Validation failed"},"401":{"content":{"application/json":{"schema":{"$ref":"#/components/schemas/ApiErrorResponse"}}},"description":"Unauthorized"},"409":{"content":{"application/json":{"schema":{"$ref":"#/components/schemas/ApiErrorResponse"}}},"description":"Slug conflict"}},"tags":["admin"]}},"/api/links":{"get":{"operationId":"list_links","responses":{"200":{"content":{"application/json":{"schema":{"$ref":"#/components/schemas/LinkListResponse"}}},"description":"List links"},"401":{"content":{"application/json":{"schema":{"$ref":"#/components/schemas/ApiErrorResponse"}}},"description":"Unauthorized"}},"tags":["admin"]},"post":{"operationId":"create_link","requestBody":{"content":{"application/json":{"schema":{"$ref":"#/components/schemas/CreateLinkRequest"}}},"required":true},"responses":{"200":{"content":{"application/json":{"schema":{"$ref":"#/components/schemas/LinkResponse"}}},"description":"Link created"},"400":{"content":{"application/json":{"schema":{"$ref":"#/components/schemas/ApiErrorResponse"}}},"description":"Validation failed"},"401":{"content":{"application/json":{"schema":{"$ref":"#/components/schemas/ApiErrorResponse"}}},"description":"Unauthorized"},"409":{"content":{"application/json":{"schema":{"$ref":"#/components/schemas/ApiErrorResponse"}}},"description":"Code conflict"}},"tags":["admin"]}},"/api/links/{id}":{"get":{"operationId":"get_link","parameters":[{"description":"Link identifier","in":"path","name":"id","required":true,"schema":{"format":"uuid","type":"string"}}],"responses":{"200":{"content":{"application/json":{"schema":{"$ref":"#/components/schemas/LinkResponse"}}},"description":"Link details"},"401":{"content":{"application/json":{"schema":{"$ref":"#/components/schemas/ApiErrorResponse"}}},"description":"Unauthorized"},"404":{"content":{"application/json":{"schema":{"$ref":"#/components/schemas/ApiErrorResponse"}}},"description":"Link not found"}},"tags":["admin"]},"patch":{"operationId":"update_link","parameters":[{"description":"Link identifier","in":"path","name":"id","required":true,"schema":{"format":"uuid","type":"string"}}],"requestBody":{"content":{"application/json":{"schema":{"$ref":"#/components/schemas/UpdateLinkRequest"}}},"required":true},"responses":{"200":{"content":{"application/json":{"schema":{"$ref":"#/components/schemas/LinkResponse"}}},"description":"Link updated"},"400":{"content":{"application/json":{"schema":{"$ref":"#/components/schemas/ApiErrorResponse"}}},"description":"Validation failed"},"401":{"content":{"application/json":{"schema":{"$ref":"#/components/schemas/ApiErrorResponse"}}},"description":"Unauthorized"},"404":{"content":{"application/json":{"schema":{"$ref":"#/components/schemas/ApiErrorResponse"}}},"description":"Link not found"},"409":{"content":{"application/json":{"schema":{"$ref":"#/components/schemas/ApiErrorResponse"}}},"description":"Code conflict"}},"tags":["admin"]}},"/api/links/{id}/disable":{"post":{"operationId":"disable_link","parameters":[{"description":"Link identifier","in":"path","name":"id","required":true,"schema":{"format":"uuid","type":"string"}}],"responses":{"200":{"content":{"application/json":{"schema":{"$ref":"#/components/schemas/LinkResponse"}}},"description":"Link disabled"},"401":{"content":{"application/json":{"schema":{"$ref":"#/components/schemas/ApiErrorResponse"}}},"description":"Unauthorized"},"404":{"content":{"application/json":{"schema":{"$ref":"#/components/schemas/ApiErrorResponse"}}},"description":"Link not found"}},"tags":["admin"]}},"/api/links/{id}/retarget":{"post":{"operationId":"retarget_link","parameters":[{"description":"Link identifier","in":"path","name":"id","required":true,"schema":{"format":"uuid","type":"string"}}],"requestBody":{"content":{"application/json":{"schema":{"$ref":"#/components/schemas/RetargetLinkRequest"}}},"required":true},"responses":{"200":{"content":{"application/json":{"schema":{"$ref":"#/components/schemas/LinkResponse"}}},"description":"Link retargeted"},"400":{"content":{"application/json":{"schema":{"$ref":"#/components/schemas/ApiErrorResponse"}}},"description":"Validation failed"},"401":{"content":{"application/json":{"schema":{"$ref":"#/components/schemas/ApiErrorResponse"}}},"description":"Unauthorized"},"404":{"content":{"application/json":{"schema":{"$ref":"#/components/schemas/ApiErrorResponse"}}},"description":"Link not found"}},"tags":["admin"]}},"/api/links/{id}/stats":{"get":{"operationId":"link_stats","parameters":[{"description":"Link identifier","in":"path","name":"id","required":true,"schema":{"format":"uuid","type":"string"}}],"responses":{"200":{"content":{"application/json":{"schema":{"$ref":"#/components/schemas/LinkStatsResponse"}}},"description":"Link analytics"},"401":{"content":{"application/json":{"schema":{"$ref":"#/components/schemas/ApiErrorResponse"}}},"description":"Unauthorized"},"404":{"content":{"application/json":{"schema":{"$ref":"#/components/schemas/ApiErrorResponse"}}},"description":"Link not found"}},"tags":["admin"]}},"/healthz":{"get":{"operationId":"healthz","responses":{"200":{"content":{"application/json":{"schema":{"$ref":"#/components/schemas/HealthResponse"}}},"description":"Service and database are healthy"},"500":{"content":{"application/json":{"schema":{"$ref":"#/components/schemas/ApiErrorResponse"}}},"description":"Health check failed"}},"tags":["public"]}},"/openapi.json":{"get":{"operationId":"openapi_json","responses":{"200":{"content":{"application/json":{"schema":{}}},"description":"OpenAPI specification"}},"tags":["admin"]}},"/qr/{code}.svg":{"get":{"operationId":"qr_svg","parameters":[{"description":"Short code","in":"path","name":"code","required":true,"schema":{"type":"string"}}],"responses":{"200":{"description":"SVG QR code"},"400":{"content":{"application/json":{"schema":{"$ref":"#/components/schemas/ApiErrorResponse"}}},"description":"Invalid code"}},"tags":["public"]}},"/{code}":{"get":{"operationId":"resolve_link","parameters":[{"description":"Short code","in":"path","name":"code","required":true,"schema":{"type":"string"}}],"responses":{"301":{"description":"Permanent redirect"},"302":{"description":"Found redirect"},"307":{"description":"Temporary redirect"},"308":{"description":"Permanent redirect"},"404":{"content":{"application/json":{"schema":{"$ref":"#/components/schemas/ApiErrorResponse"}}},"description":"Link not found"}},"tags":["public"]}}},"tags":[{"description":"Public redirect and QR endpoints","name":"public"},{"description":"Administrative APIs for campaigns, links, and analytics","name":"admin"}]}