{"openapi":"3.0.3","info":{"title":"uTraLink API","version":"1.0.0","description":"Programmatic access to uTraLink QR codes. Authenticate with an API key sent as a Bearer token \u2014 create keys under Dashboard \u2192 Developers \u2192 API Keys."},"servers":[{"url":"https:\/\/www.utralink.com\/api\/v1"}],"security":[{"bearerAuth":[]}],"paths":{"\/me":{"get":{"summary":"Current user","responses":{"200":{"description":"Authenticated user","content":{"application\/json":{"schema":{"$ref":"#\/components\/schemas\/User"}}}},"401":{"$ref":"#\/components\/responses\/Unauthorized"}}}},"\/qrcode-types":{"get":{"summary":"Available QR code types","responses":{"200":{"description":"Type list","content":{"application\/json":{"schema":{"type":"array","items":{"$ref":"#\/components\/schemas\/QRCodeType"}}}}},"401":{"$ref":"#\/components\/responses\/Unauthorized"}}}},"\/qrcodes":{"get":{"summary":"List QR codes","parameters":[{"name":"page","in":"query","schema":{"type":"integer","default":1}},{"name":"per_page","in":"query","schema":{"type":"integer","default":20,"maximum":100}}],"responses":{"200":{"description":"Paginated QR code list","content":{"application\/json":{"schema":{"$ref":"#\/components\/schemas\/QRCodePage"}}}},"401":{"$ref":"#\/components\/responses\/Unauthorized"}}},"post":{"summary":"Create a QR code","requestBody":{"required":true,"content":{"application\/json":{"schema":{"$ref":"#\/components\/schemas\/QRCodeInput"}}}},"responses":{"201":{"description":"Created QR code","content":{"application\/json":{"schema":{"$ref":"#\/components\/schemas\/QRCode"}}}},"401":{"$ref":"#\/components\/responses\/Unauthorized"},"403":{"$ref":"#\/components\/responses\/Forbidden"},"422":{"$ref":"#\/components\/responses\/ValidationError"}}}},"\/qrcodes\/bulk":{"post":{"summary":"Bulk create QR codes","description":"Create up to 50 QR codes in one call. Each item validates independently \u2014 failures do not abort the batch; per-item results are returned for selective retry.","requestBody":{"required":true,"content":{"application\/json":{"schema":{"type":"object","required":["items"],"properties":{"items":{"type":"array","maxItems":50,"items":{"$ref":"#\/components\/schemas\/QRCodeInput"}}}}}}},"responses":{"201":{"description":"At least one item created","content":{"application\/json":{"schema":{"$ref":"#\/components\/schemas\/BulkResult"}}}},"401":{"$ref":"#\/components\/responses\/Unauthorized"},"403":{"$ref":"#\/components\/responses\/Forbidden"},"422":{"description":"All items failed, or malformed batch","content":{"application\/json":{"schema":{"$ref":"#\/components\/schemas\/BulkResult"}}}}}}},"\/qrcodes\/{id}\/stats":{"parameters":[{"name":"id","in":"path","required":true,"schema":{"type":"integer"}}],"get":{"summary":"Scan statistics for a QR code","parameters":[{"name":"days","in":"query","schema":{"type":"integer","default":30,"minimum":1,"maximum":365}}],"responses":{"200":{"description":"Scan stats for the requested window","content":{"application\/json":{"schema":{"$ref":"#\/components\/schemas\/ScanStats"}}}},"401":{"$ref":"#\/components\/responses\/Unauthorized"},"404":{"$ref":"#\/components\/responses\/NotFound"}}}},"\/qrcodes\/{id}":{"parameters":[{"name":"id","in":"path","required":true,"schema":{"type":"integer"}}],"get":{"summary":"Get a QR code","responses":{"200":{"description":"QR code","content":{"application\/json":{"schema":{"$ref":"#\/components\/schemas\/QRCode"}}}},"401":{"$ref":"#\/components\/responses\/Unauthorized"},"404":{"$ref":"#\/components\/responses\/NotFound"}}},"patch":{"summary":"Update a QR code","requestBody":{"required":true,"content":{"application\/json":{"schema":{"$ref":"#\/components\/schemas\/QRCodeInput"}}}},"responses":{"200":{"description":"Updated QR code","content":{"application\/json":{"schema":{"$ref":"#\/components\/schemas\/QRCode"}}}},"401":{"$ref":"#\/components\/responses\/Unauthorized"},"403":{"$ref":"#\/components\/responses\/Forbidden"},"404":{"$ref":"#\/components\/responses\/NotFound"},"422":{"$ref":"#\/components\/responses\/ValidationError"}}},"delete":{"summary":"Delete a QR code","responses":{"200":{"description":"Deleted","content":{"application\/json":{"schema":{"type":"object","properties":{"deleted":{"type":"boolean"},"id":{"type":"integer"}}}}}},"401":{"$ref":"#\/components\/responses\/Unauthorized"},"403":{"$ref":"#\/components\/responses\/Forbidden"},"404":{"$ref":"#\/components\/responses\/NotFound"}}}}},"components":{"securitySchemes":{"bearerAuth":{"type":"http","scheme":"bearer","description":"API key created in Dashboard \u2192 Developers \u2192 API Keys. Read-only keys work on GET endpoints; mutations return 403."}},"responses":{"Unauthorized":{"description":"Missing or invalid API key"},"Forbidden":{"description":"API key lacks the required scope (read-only keys cannot mutate)"},"NotFound":{"description":"Resource not found or not owned by the key's user"},"ValidationError":{"description":"Validation failed \u2014 errors keyed by field"}},"schemas":{"User":{"type":"object","properties":{"id":{"type":"integer"},"name":{"type":"string"},"email":{"type":"string"}}},"QRCodeType":{"type":"object","properties":{"slug":{"type":"string","example":"url"},"name":{"type":"string","example":"Dynamic URL"},"dynamic":{"type":"boolean"}}},"QRCodeInput":{"type":"object","required":["type","data"],"properties":{"name":{"type":"string","example":"My QR"},"type":{"type":"string","example":"url","description":"Slug from \/qrcode-types"},"data":{"type":"object","example":{"url":"https:\/\/example.com"},"description":"Type-specific payload"},"design":{"type":"object","description":"Optional design overrides"}}},"QRCode":{"type":"object","properties":{"id":{"type":"integer"},"name":{"type":"string"},"type":{"type":"string"},"status":{"type":"string"},"scans":{"type":"integer"},"scan_url":{"type":"string","nullable":true},"svg_url":{"type":"string"},"png_url":{"type":"string"},"data":{"type":"object"},"created_at":{"type":"string","format":"date-time"},"updated_at":{"type":"string","format":"date-time"}}},"ScanStats":{"type":"object","properties":{"id":{"type":"integer"},"days":{"type":"integer"},"total":{"type":"integer"},"daily":{"type":"object","additionalProperties":{"type":"integer"},"example":{"2026-10-01":12,"2026-10-02":8}},"top_countries":{"type":"object","additionalProperties":{"type":"integer"}},"top_devices":{"type":"object","additionalProperties":{"type":"integer"}},"top_browsers":{"type":"object","additionalProperties":{"type":"integer"}}}},"BulkResult":{"type":"object","properties":{"created":{"type":"integer"},"failed":{"type":"integer"},"results":{"type":"array","items":{"type":"object","properties":{"index":{"type":"integer"},"ok":{"type":"boolean"},"qrcode":{"$ref":"#\/components\/schemas\/QRCode"},"errors":{"type":"object"}}}}}},"QRCodePage":{"type":"object","properties":{"data":{"type":"array","items":{"$ref":"#\/components\/schemas\/QRCode"}},"meta":{"type":"object","properties":{"current_page":{"type":"integer"},"last_page":{"type":"integer"},"total":{"type":"integer"}}}}}}}}