{"openapi":"3.1.0","info":{"title":"QRA API","version":"1.0.0","description":"Programmatic access to dynamic QR codes, scan analytics, and workspace metadata. All requests require an API key with the `Authorization: Bearer qrf_live_…` header.","contact":{"email":"support@qra.cc"}},"servers":[{"url":"https://www.qra.cc/api/v1","description":"Production"}],"components":{"securitySchemes":{"bearerAuth":{"type":"http","scheme":"bearer","bearerFormat":"qrf_live_*"}},"schemas":{"QRCode":{"type":"object","properties":{"id":{"type":"string","format":"uuid"},"slug":{"type":"string","example":"abc123xyz"},"name":{"type":"string","example":"Riyadh Mall — counter sticker"},"type":{"type":"string","enum":["url","vcard","wifi","sms","whatsapp","email","file","multilink","social","app","event","business","coupon","feedback","landing_page","product","vcard_plus","menu","images","video","mp3","playlist"]},"destination":{"type":"string","nullable":true,"example":"https://example.com/menu"},"status":{"type":"string","enum":["active","paused","expired"]},"created_at":{"type":"string","format":"date-time"},"updated_at":{"type":"string","format":"date-time"}}},"Pagination":{"type":"object","properties":{"page":{"type":"integer"},"limit":{"type":"integer"},"total":{"type":"integer"},"pages":{"type":"integer"}}},"Error":{"type":"object","properties":{"error":{"type":"string"},"code":{"type":"string"}}},"Scan":{"type":"object","properties":{"id":{"type":"integer"},"ts":{"type":"string","format":"date-time"},"country":{"type":"string","nullable":true,"example":"SA"},"city":{"type":"string","nullable":true,"example":"Riyadh"},"device":{"type":"string","enum":["mobile","tablet","desktop","unknown"]},"os":{"type":"string","example":"iOS"},"browser":{"type":"string","example":"Safari"},"referrer":{"type":"string","nullable":true}}}}},"security":[{"bearerAuth":[]}],"paths":{"/qr":{"get":{"summary":"List QR codes","description":"Paginated list of QR codes in the API key's workspace.","parameters":[{"name":"page","in":"query","schema":{"type":"integer","default":1}},{"name":"limit","in":"query","schema":{"type":"integer","default":20,"maximum":100}}],"responses":{"200":{"description":"Paginated list","content":{"application/json":{"schema":{"type":"object","properties":{"data":{"type":"array","items":{"$ref":"#/components/schemas/QRCode"}},"pagination":{"$ref":"#/components/schemas/Pagination"}}}}},"headers":{"X-RateLimit-Plan":{"schema":{"type":"string"}},"X-RateLimit-Limit":{"schema":{"type":"string"}}}},"401":{"description":"Missing or invalid API key","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"}}}}}},"post":{"summary":"Create a QR code","description":"Creates a dynamic QR code in the API key's workspace. Subject to the plan's code-count limit.","requestBody":{"required":true,"content":{"application/json":{"schema":{"type":"object","required":["name"],"properties":{"name":{"type":"string","example":"Q4 catalogue insert"},"type":{"type":"string","default":"url"},"destination":{"type":"string","example":"https://example.com/catalogue"}}}}}},"responses":{"201":{"description":"Created","content":{"application/json":{"schema":{"$ref":"#/components/schemas/QRCode"}}}},"400":{"description":"Validation error","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"}}}},"403":{"description":"Plan limit reached","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"}}}}}}},"/qr/{id}":{"parameters":[{"name":"id","in":"path","required":true,"schema":{"type":"string","format":"uuid"}}],"get":{"summary":"Get a QR code","responses":{"200":{"description":"Found","content":{"application/json":{"schema":{"$ref":"#/components/schemas/QRCode"}}}},"404":{"description":"Not found"}}},"patch":{"summary":"Update a QR code","description":"Update name, destination, or status. Other fields are read-only via the public API for now.","requestBody":{"content":{"application/json":{"schema":{"type":"object","properties":{"name":{"type":"string"},"destination":{"type":"string"},"status":{"type":"string","enum":["active","paused","expired"]}}}}}},"responses":{"200":{"description":"Updated","content":{"application/json":{"schema":{"$ref":"#/components/schemas/QRCode"}}}}}},"delete":{"summary":"Delete a QR code","responses":{"204":{"description":"Deleted"}}}},"/qr/{id}/scans":{"parameters":[{"name":"id","in":"path","required":true,"schema":{"type":"string","format":"uuid"}}],"get":{"summary":"List raw scans for a QR","description":"Paginated list of raw scan rows. Use ?since= and ?until= (ISO timestamps) to filter.","parameters":[{"name":"page","in":"query","schema":{"type":"integer","default":1}},{"name":"limit","in":"query","schema":{"type":"integer","default":50,"maximum":500}},{"name":"since","in":"query","schema":{"type":"string","format":"date-time"}},{"name":"until","in":"query","schema":{"type":"string","format":"date-time"}}],"responses":{"200":{"description":"Paginated scans","content":{"application/json":{"schema":{"type":"object","properties":{"data":{"type":"array","items":{"$ref":"#/components/schemas/Scan"}},"pagination":{"$ref":"#/components/schemas/Pagination"}}}}}}}}}}}