{"openapi":"3.1.0","info":{"title":"relynkr API","version":"0.1.0","description":"API documentation for relynkr engagement tracking, managed short links, redirects, QR codes, and analytics. API key access is planned as a product feature; current endpoints use the signed-in session cookie."},"servers":[{"url":"https://relynkr.com","description":"Current application host"}],"tags":[{"name":"Account","description":"Signed-in account and current plan usage."},{"name":"Links","description":"Tracked short-link management."},{"name":"Analytics","description":"Engagement reports and exports."},{"name":"Redirects","description":"Public redirect endpoints."}],"paths":{"/api/me":{"get":{"summary":"Get current account","description":"Returns the signed-in user and current plan usage.","tags":["Account"],"security":[{"sessionCookie":[]}],"responses":{"200":{"description":"Current account","content":{"application/json":{"schema":{"$ref":"#/components/schemas/MeResponse"}}}},"401":{"description":"Unauthenticated","content":{"application/json":{"schema":{"$ref":"#/components/schemas/ErrorResponse"}}}}}}},"/api/links":{"get":{"summary":"List tracked links","description":"Returns links owned by the signed-in account, optionally filtered by short link or destination URL.","tags":["Links"],"security":[{"sessionCookie":[]}],"parameters":[{"name":"q","in":"query","required":false,"schema":{"type":"string"},"description":"Search text for short links or destination URLs."}],"responses":{"200":{"description":"Tracked links","content":{"application/json":{"schema":{"$ref":"#/components/schemas/ListLinksResponse"}}}},"401":{"description":"Unauthenticated","content":{"application/json":{"schema":{"$ref":"#/components/schemas/ErrorResponse"}}}}}},"post":{"summary":"Create a tracked link","description":"Creates a new tracked short link if the account has remaining plan capacity.","tags":["Links"],"security":[{"sessionCookie":[]}],"requestBody":{"required":true,"content":{"application/json":{"schema":{"$ref":"#/components/schemas/LinkInput"}}}},"responses":{"201":{"description":"Created link","content":{"application/json":{"schema":{"$ref":"#/components/schemas/LinkResponse"}}}},"400":{"description":"Validation failed","content":{"application/json":{"schema":{"$ref":"#/components/schemas/ErrorResponse"}}}},"401":{"description":"Unauthenticated","content":{"application/json":{"schema":{"$ref":"#/components/schemas/ErrorResponse"}}}},"403":{"description":"Plan limit reached","content":{"application/json":{"schema":{"$ref":"#/components/schemas/ErrorResponse"}}}},"409":{"description":"Short link already exists","content":{"application/json":{"schema":{"$ref":"#/components/schemas/ErrorResponse"}}}}}}},"/api/links/{id}":{"get":{"summary":"Get a tracked link","tags":["Links"],"security":[{"sessionCookie":[]}],"parameters":[{"name":"id","in":"path","required":true,"schema":{"type":"string"},"description":"Tracked link ID."}],"responses":{"200":{"description":"Tracked link","content":{"application/json":{"schema":{"$ref":"#/components/schemas/LinkResponse"}}}},"401":{"description":"Unauthenticated","content":{"application/json":{"schema":{"$ref":"#/components/schemas/ErrorResponse"}}}},"404":{"description":"Link not found","content":{"application/json":{"schema":{"$ref":"#/components/schemas/ErrorResponse"}}}}}},"put":{"summary":"Update a tracked link","description":"Updates destination, short link, redirect type, or active status.","tags":["Links"],"security":[{"sessionCookie":[]}],"parameters":[{"name":"id","in":"path","required":true,"schema":{"type":"string"},"description":"Tracked link ID."}],"requestBody":{"required":true,"content":{"application/json":{"schema":{"$ref":"#/components/schemas/LinkUpdateInput"}}}},"responses":{"200":{"description":"Updated link","content":{"application/json":{"schema":{"$ref":"#/components/schemas/LinkResponse"}}}},"400":{"description":"Validation failed","content":{"application/json":{"schema":{"$ref":"#/components/schemas/ErrorResponse"}}}},"401":{"description":"Unauthenticated","content":{"application/json":{"schema":{"$ref":"#/components/schemas/ErrorResponse"}}}},"404":{"description":"Link not found","content":{"application/json":{"schema":{"$ref":"#/components/schemas/ErrorResponse"}}}},"409":{"description":"Short link already exists","content":{"application/json":{"schema":{"$ref":"#/components/schemas/ErrorResponse"}}}}}},"delete":{"summary":"Delete a tracked link","tags":["Links"],"security":[{"sessionCookie":[]}],"parameters":[{"name":"id","in":"path","required":true,"schema":{"type":"string"},"description":"Tracked link ID."}],"responses":{"200":{"description":"Deleted","content":{"application/json":{"schema":{"$ref":"#/components/schemas/OkResponse"}}}},"401":{"description":"Unauthenticated","content":{"application/json":{"schema":{"$ref":"#/components/schemas/ErrorResponse"}}}},"404":{"description":"Link not found","content":{"application/json":{"schema":{"$ref":"#/components/schemas/ErrorResponse"}}}}}}},"/api/links/{id}/qr":{"get":{"summary":"Download QR code","description":"Returns a QR code for a tracked link. Use the format query parameter to download PNG, JPEG, or SVG.","tags":["Links"],"security":[{"sessionCookie":[]}],"parameters":[{"name":"id","in":"path","required":true,"schema":{"type":"string"},"description":"Tracked link ID."},{"name":"format","in":"query","required":false,"schema":{"type":"string","enum":["png","jpeg","svg"],"default":"png"},"description":"QR code image format."},{"name":"download","in":"query","required":false,"schema":{"type":"string","enum":["1"]},"description":"Set to 1 to return the QR code as an attachment."}],"responses":{"200":{"description":"QR code image","content":{"image/png":{"schema":{"type":"string","format":"binary"}},"image/jpeg":{"schema":{"type":"string","format":"binary"}},"image/svg+xml":{"schema":{"type":"string","format":"binary"}}}},"401":{"description":"Unauthenticated","content":{"application/json":{"schema":{"$ref":"#/components/schemas/ErrorResponse"}}}},"404":{"description":"Link not found","content":{"application/json":{"schema":{"$ref":"#/components/schemas/ErrorResponse"}}}}}},"post":{"summary":"Render QR code preview","description":"Renders a QR code using draft design settings supplied in the JSON body. Used by the editor preview and draft downloads.","tags":["Links"],"security":[{"sessionCookie":[]}],"parameters":[{"name":"id","in":"path","required":true,"schema":{"type":"string"},"description":"Tracked link ID."}],"requestBody":{"required":true,"content":{"application/json":{"schema":{"type":"object","properties":{"design":{"$ref":"#/components/schemas/QrDesign"},"format":{"type":"string","enum":["png","jpeg","svg"],"default":"png"},"download":{"type":"boolean","default":false}},"required":["design"]}}}},"responses":{"200":{"description":"QR code image","content":{"image/png":{"schema":{"type":"string","format":"binary"}},"image/jpeg":{"schema":{"type":"string","format":"binary"}},"image/svg+xml":{"schema":{"type":"string","format":"binary"}}}},"400":{"description":"Validation failed","content":{"application/json":{"schema":{"$ref":"#/components/schemas/ErrorResponse"}}}},"401":{"description":"Unauthenticated","content":{"application/json":{"schema":{"$ref":"#/components/schemas/ErrorResponse"}}}},"404":{"description":"Link not found","content":{"application/json":{"schema":{"$ref":"#/components/schemas/ErrorResponse"}}}}}}},"/api/links/{id}/qr/logo":{"post":{"summary":"Upload QR logo","description":"Uploads a PNG or JPEG logo asset for a tracked link and returns logo metadata to include in qrDesign.","tags":["Links"],"security":[{"sessionCookie":[]}],"parameters":[{"name":"id","in":"path","required":true,"schema":{"type":"string"},"description":"Tracked link ID."}],"requestBody":{"required":true,"content":{"multipart/form-data":{"schema":{"type":"object","properties":{"logo":{"type":"string","format":"binary"}},"required":["logo"]}}}},"responses":{"201":{"description":"Uploaded QR logo","content":{"application/json":{"schema":{"$ref":"#/components/schemas/QrLogoUploadResponse"}}}},"400":{"description":"Validation failed","content":{"application/json":{"schema":{"$ref":"#/components/schemas/ErrorResponse"}}}},"401":{"description":"Unauthenticated","content":{"application/json":{"schema":{"$ref":"#/components/schemas/ErrorResponse"}}}},"404":{"description":"Link not found","content":{"application/json":{"schema":{"$ref":"#/components/schemas/ErrorResponse"}}}}}}},"/api/analytics":{"get":{"summary":"Get engagement analytics","description":"Returns daily engagement trends and breakdowns for the signed-in account or a single tracked link.","tags":["Analytics"],"security":[{"sessionCookie":[]}],"parameters":[{"name":"range","in":"query","required":false,"schema":{"type":"integer","enum":[7,30,90]},"description":"Preset number of days to report."},{"name":"from","in":"query","required":false,"schema":{"type":"string","format":"date"},"description":"Explicit report start date."},{"name":"to","in":"query","required":false,"schema":{"type":"string","format":"date"},"description":"Explicit report end date."},{"name":"linkId","in":"query","required":false,"schema":{"type":"array","items":{"type":"string"}},"style":"form","explode":true,"description":"Limit the report to one or more tracked links owned by the signed-in account. Repeat linkId for multiple links."}],"responses":{"200":{"description":"Analytics report","content":{"application/json":{"schema":{"$ref":"#/components/schemas/AnalyticsResponse"}}}},"400":{"description":"Invalid date range","content":{"application/json":{"schema":{"$ref":"#/components/schemas/ErrorResponse"}}}},"401":{"description":"Unauthenticated","content":{"application/json":{"schema":{"$ref":"#/components/schemas/ErrorResponse"}}}},"404":{"description":"Link not found","content":{"application/json":{"schema":{"$ref":"#/components/schemas/ErrorResponse"}}}}}}},"/api/analytics/export":{"get":{"summary":"Export engagement analytics","description":"Downloads the analytics report as CSV.","tags":["Analytics"],"security":[{"sessionCookie":[]}],"parameters":[{"name":"range","in":"query","required":false,"schema":{"type":"integer","enum":[7,30,90]},"description":"Preset number of days to report."},{"name":"from","in":"query","required":false,"schema":{"type":"string","format":"date"},"description":"Explicit report start date."},{"name":"to","in":"query","required":false,"schema":{"type":"string","format":"date"},"description":"Explicit report end date."},{"name":"linkId","in":"query","required":false,"schema":{"type":"array","items":{"type":"string"}},"style":"form","explode":true,"description":"Limit the report to one or more tracked links owned by the signed-in account. Repeat linkId for multiple links."}],"responses":{"200":{"description":"Analytics CSV","content":{"text/csv":{"schema":{"type":"string"}}}},"400":{"description":"Invalid date range","content":{"application/json":{"schema":{"$ref":"#/components/schemas/ErrorResponse"}}}},"401":{"description":"Unauthenticated","content":{"application/json":{"schema":{"$ref":"#/components/schemas/ErrorResponse"}}}},"404":{"description":"Link not found","content":{"application/json":{"schema":{"$ref":"#/components/schemas/ErrorResponse"}}}}}}},"/r/{slug}":{"get":{"summary":"Follow a public short link","description":"Records engagement and redirects to the configured destination with the link's 301 or 302 status when the link is active and not expired.","tags":["Redirects"],"parameters":[{"name":"slug","in":"path","required":true,"schema":{"type":"string"},"description":"Public short link value."}],"responses":{"301":{"description":"Permanent redirect"},"302":{"description":"Temporary redirect"},"404":{"description":"Link not found","content":{"application/json":{"schema":{"$ref":"#/components/schemas/ErrorResponse"}}}},"410":{"description":"Link inactive or expired","content":{"application/json":{"schema":{"$ref":"#/components/schemas/ErrorResponse"}}}}}}}},"components":{"securitySchemes":{"sessionCookie":{"type":"apiKey","in":"cookie","name":"better-auth.session_token","description":"Current signed-in browser session. Dedicated API keys are planned for API access tiers."},"apiKey":{"type":"apiKey","in":"header","name":"Authorization","description":"Planned bearer-token authentication for API access."}},"schemas":{"OkResponse":{"type":"object","properties":{"ok":{"type":"boolean","const":true}},"required":["ok"]},"ErrorResponse":{"type":"object","properties":{"ok":{"type":"boolean","const":false},"error":{"type":"object","properties":{"code":{"type":"string"},"message":{"type":"string"}},"required":["code","message"]}},"required":["ok","error"]},"LinkInput":{"type":"object","properties":{"slug":{"type":"string","example":"spring-menu"},"targetUrl":{"type":"string","format":"uri","example":"https://example.com/menu"},"redirectStatus":{"type":"integer","enum":[301,302],"default":302},"forwardQuery":{"type":"boolean","default":false},"active":{"type":"boolean","default":true},"expiresAt":{"type":"string","format":"date-time","nullable":true},"expiresAtTimezone":{"type":"string","example":"Australia/Brisbane","nullable":true},"qrDesign":{"$ref":"#/components/schemas/QrDesign"}},"required":["slug","targetUrl"]},"LinkUpdateInput":{"type":"object","properties":{"slug":{"type":"string","example":"spring-menu"},"targetUrl":{"type":"string","format":"uri","example":"https://example.com/menu"},"redirectStatus":{"type":"integer","enum":[301,302]},"forwardQuery":{"type":"boolean"},"active":{"type":"boolean"},"expiresAt":{"type":"string","format":"date-time","nullable":true},"expiresAtTimezone":{"type":"string","example":"Australia/Brisbane","nullable":true},"qrDesign":{"$ref":"#/components/schemas/QrDesign"}}},"QrDesign":{"type":"object","properties":{"foregroundColor":{"type":"string","pattern":"^#[0-9a-fA-F]{6}$","default":"#172033"},"backgroundColor":{"type":"string","pattern":"^#[0-9a-fA-F]{6}$","default":"#ffffff"},"transparentBackground":{"type":"boolean","default":false},"customEyeColor":{"type":"boolean","default":false},"eyeFrameColor":{"type":"string","pattern":"^#[0-9a-fA-F]{6}$","default":"#172033"},"eyeBallColor":{"type":"string","pattern":"^#[0-9a-fA-F]{6}$","default":"#172033"},"bodyShape":{"type":"string","enum":["square","rounded","dots","diamond","vertical","horizontal"],"default":"square"},"eyeFrameShape":{"type":"string","enum":["square","rounded","circle","diamond","leaf"],"default":"square"},"eyeBallShape":{"type":"string","enum":["square","rounded","circle","diamond","leaf"],"default":"square"},"framePreset":{"type":"string","enum":["none","border","caption","banner"],"default":"none"},"frameColor":{"type":"string","pattern":"^#[0-9a-fA-F]{6}$","default":"#172033"},"frameText":{"type":"string","maxLength":40,"default":"Scan me"},"logo":{"$ref":"#/components/schemas/QrLogo"}}},"QrLogo":{"type":"object","properties":{"key":{"type":"string","example":"qr-logos/user-id/link-id/logo.png"},"contentType":{"type":"string","enum":["image/png","image/jpeg"]},"name":{"type":"string","example":"brand-logo.png"},"sizeRatio":{"type":"number","minimum":0.12,"maximum":0.24,"default":0.2},"backgroundPadding":{"type":"boolean","default":true}},"required":["key","contentType","name","sizeRatio","backgroundPadding"]},"QrLogoUploadResponse":{"type":"object","properties":{"ok":{"type":"boolean","const":true},"logo":{"$ref":"#/components/schemas/QrLogo"}},"required":["ok","logo"]},"Link":{"type":"object","properties":{"id":{"type":"string"},"slug":{"type":"string"},"shortUrl":{"type":"string","format":"uri"},"targetUrl":{"type":"string","format":"uri"},"redirectStatus":{"type":"integer","enum":[301,302]},"forwardQuery":{"type":"boolean"},"active":{"type":"boolean"},"availability":{"type":"string","enum":["active","inactive","expired"]},"expiresAt":{"type":"string","format":"date-time","nullable":true},"expiresAtTimezone":{"type":"string","nullable":true},"qrDesign":{"$ref":"#/components/schemas/QrDesign"},"clickCount":{"type":"integer"},"createdAt":{"type":"string","format":"date-time"},"updatedAt":{"type":"string","format":"date-time"}},"required":["id","slug","shortUrl","targetUrl","redirectStatus","forwardQuery","active","availability","expiresAt","expiresAtTimezone","qrDesign","clickCount","createdAt","updatedAt"]},"PlanUsage":{"type":"object","properties":{"plan":{"type":"string","enum":["starter","growth"]},"planLabel":{"type":"string"},"linkLimit":{"type":"integer"},"linkCount":{"type":"integer"},"remainingLinks":{"type":"integer"},"canCreateLink":{"type":"boolean"},"nearingLimit":{"type":"boolean"},"percentUsed":{"type":"integer"}},"required":["plan","planLabel","linkLimit","linkCount","remainingLinks","canCreateLink","nearingLimit","percentUsed"]},"LinkResponse":{"type":"object","properties":{"ok":{"type":"boolean","const":true},"link":{"$ref":"#/components/schemas/Link"}},"required":["ok","link"]},"ListLinksResponse":{"type":"object","properties":{"ok":{"type":"boolean","const":true},"links":{"type":"array","items":{"$ref":"#/components/schemas/Link"}},"usage":{"$ref":"#/components/schemas/PlanUsage"}},"required":["ok","links","usage"]},"MeResponse":{"type":"object","properties":{"ok":{"type":"boolean","const":true},"user":{"type":"object","properties":{"id":{"type":"string"},"name":{"type":"string"},"email":{"type":"string","format":"email"}}},"usage":{"$ref":"#/components/schemas/PlanUsage"}},"required":["ok","user","usage"]},"AnalyticsResponse":{"type":"object","properties":{"ok":{"type":"boolean","const":true},"report":{"$ref":"#/components/schemas/AnalyticsReport"}},"required":["ok","report"]},"AnalyticsRange":{"type":"object","properties":{"from":{"type":"string","format":"date"},"to":{"type":"string","format":"date"}},"required":["from","to"]},"AnalyticsBucket":{"type":"object","properties":{"label":{"type":"string"},"total":{"type":"integer"},"qrScans":{"type":"integer"},"linkClicks":{"type":"integer"}},"required":["label","total","qrScans","linkClicks"]},"AnalyticsSeriesPoint":{"allOf":[{"$ref":"#/components/schemas/AnalyticsBucket"},{"type":"object","properties":{"day":{"type":"string","format":"date"},"botCount":{"type":"integer"}},"required":["day","botCount"]}]},"AnalyticsTopLinkBucket":{"allOf":[{"$ref":"#/components/schemas/AnalyticsBucket"},{"type":"object","properties":{"linkId":{"type":"string"},"slug":{"type":"string"},"shortUrl":{"type":"string","format":"uri"},"targetUrl":{"type":"string","format":"uri"}},"required":["linkId","slug","shortUrl","targetUrl"]}]},"AnalyticsSummary":{"type":"object","properties":{"total":{"type":"integer"},"qrScans":{"type":"integer"},"linkClicks":{"type":"integer"},"botCount":{"type":"integer"},"topLink":{"anyOf":[{"$ref":"#/components/schemas/AnalyticsBucket"},{"type":"null"}]},"topCountry":{"anyOf":[{"$ref":"#/components/schemas/AnalyticsBucket"},{"type":"null"}]}},"required":["total","qrScans","linkClicks","botCount","topLink","topCountry"]},"AnalyticsReport":{"type":"object","properties":{"range":{"$ref":"#/components/schemas/AnalyticsRange"},"summary":{"$ref":"#/components/schemas/AnalyticsSummary"},"series":{"type":"array","items":{"$ref":"#/components/schemas/AnalyticsSeriesPoint"}},"topLinks":{"type":"array","items":{"$ref":"#/components/schemas/AnalyticsTopLinkBucket"}},"countries":{"type":"array","items":{"$ref":"#/components/schemas/AnalyticsBucket"}},"cities":{"type":"array","items":{"$ref":"#/components/schemas/AnalyticsBucket"}},"referrers":{"type":"array","items":{"$ref":"#/components/schemas/AnalyticsBucket"}},"devices":{"type":"array","items":{"$ref":"#/components/schemas/AnalyticsBucket"}},"browsers":{"type":"array","items":{"$ref":"#/components/schemas/AnalyticsBucket"}},"operatingSystems":{"type":"array","items":{"$ref":"#/components/schemas/AnalyticsBucket"}}},"required":["range","summary","series","topLinks","countries","cities","referrers","devices","browsers","operatingSystems"]}}}}