{"openapi":"3.1.0","info":{"title":"Snappy API","description":"Create, manage, and measure your Snappy short links.\n\n[Open Snappy](/) · [Manage links](/dashboard)"},"servers":[{"url":"https://snappy.fyi","description":"Local Development Server","variables":{}}],"paths":{"":{"get":{"tags":["App Routes"],"parameters":[],"responses":{"200":{"description":"OK"}}}},"/api/account/domains":{"get":{"tags":["API Routes"],"parameters":[],"responses":{"200":{"description":"OK"}}},"post":{"tags":["API Routes"],"parameters":[],"responses":{"200":{"description":"OK"}}}},"/api/account/domains/verify":{"post":{"tags":["API Routes"],"parameters":[],"responses":{"200":{"description":"OK"}}}},"/api/account":{"get":{"tags":["API Routes"],"parameters":[],"responses":{"200":{"description":"OK"}}}},"/api/account/invites":{"post":{"tags":["API Routes"],"parameters":[],"responses":{"200":{"description":"OK"}}}},"/api/account/invites/accept":{"post":{"tags":["API Routes"],"parameters":[],"responses":{"200":{"description":"OK"}}}},"/api/account/keys/{id}":{"delete":{"tags":["API Routes"],"parameters":[{"name":"id","in":"path","required":true,"schema":{"type":"string"}}],"responses":{"200":{"description":"OK"}}}},"/api/account/keys":{"get":{"tags":["API Routes"],"parameters":[],"responses":{"200":{"description":"OK"}}},"post":{"tags":["API Routes"],"parameters":[],"responses":{"200":{"description":"OK"}}}},"/api/account/members":{"get":{"tags":["API Routes"],"parameters":[],"responses":{"200":{"description":"OK"}}}},"/api/account/members/{id}":{"delete":{"tags":["API Routes"],"parameters":[{"name":"id","in":"path","required":true,"schema":{"type":"string"}}],"responses":{"200":{"description":"OK"}}}},"/api/account/namespace":{"put":{"tags":["API Routes"],"parameters":[],"responses":{"200":{"description":"OK"}}}},"/api/account/workspace":{"put":{"tags":["API Routes"],"parameters":[],"responses":{"200":{"description":"OK"}}}},"/api/account/workspaces":{"get":{"tags":["API Routes"],"parameters":[],"responses":{"200":{"description":"OK"}}},"post":{"tags":["API Routes"],"parameters":[],"responses":{"200":{"description":"OK"}}}},"/api/auth/{all}":{"get":{"tags":["API Routes"],"parameters":[{"name":"all","in":"path","required":true,"schema":{"type":"string"}}],"responses":{"200":{"description":"OK"}}}},"/api/backup":{"post":{"tags":["API Routes"],"parameters":[],"responses":{"200":{"description":"OK"}},"description":"Manually trigger a backup to R2","security":[{"bearerAuth":[]}]}},"/api/billing/checkout":{"post":{"tags":["API Routes"],"parameters":[],"responses":{"200":{"description":"OK"}}}},"/api/billing/portal":{"post":{"tags":["API Routes"],"parameters":[],"responses":{"200":{"description":"OK"}}}},"/api/billing/webhook":{"post":{"tags":["API Routes"],"parameters":[],"responses":{"200":{"description":"OK"}}}},"/api/link/ai":{"get":{"tags":["API Routes"],"parameters":[{"name":"url","in":"query","required":true,"schema":{"type":"string","format":"uri"},"description":"The URL to generate a slug for"}],"responses":{"200":{"description":"OK"}},"description":"Generate a slug using AI based on the URL","security":[{"bearerAuth":[]}]}},"/api/link/availability":{"get":{"tags":["API Routes"],"parameters":[],"responses":{"200":{"description":"OK"}}}},"/api/link/check":{"post":{"tags":["API Routes"],"parameters":[],"responses":{"200":{"description":"OK"}},"description":"Check target URLs for existing short links","security":[{"bearerAuth":[]}],"requestBody":{"required":true,"content":{"application/json":{"schema":{"type":"object","properties":{"cursor":{"type":"string","description":"Pagination cursor from the previous response"},"limit":{"type":"integer","default":6,"minimum":1,"maximum":10,"description":"Maximum number of links to check"},"timeout":{"type":"integer","default":6,"minimum":1,"maximum":30,"description":"Timeout in seconds for each link"}}}}}}}},"/api/link/count":{"get":{"tags":["API Routes"],"parameters":[{"name":"q","in":"query","required":false,"schema":{"type":"string"},"description":"Case-insensitive substring to match against slug, URL, comment, or tag"},{"name":"url","in":"query","required":false,"schema":{"type":"string"},"description":"Normalized target URL to match exactly"},{"name":"tag","in":"query","required":false,"schema":{"type":"string"},"description":"Exact normalized tag filter"},{"name":"status","in":"query","required":false,"schema":{"type":"string","enum":["active","expired","all"],"default":"active"},"description":"Expiration status filter"}],"responses":{"200":{"description":"OK"}},"description":"Count links matching keyword, URL, tag, and expiration status filters","security":[{"bearerAuth":[]}]}},"/api/link/create":{"post":{"tags":["API Routes"],"parameters":[],"responses":{"200":{"description":"OK"}},"description":"Create a new short link","security":[{"bearerAuth":[]}],"requestBody":{"required":true,"content":{"application/json":{"schema":{"$ref":"#/components/schemas/CreateLink"}}}}}},"/api/link/delete":{"post":{"tags":["API Routes"],"parameters":[],"responses":{"200":{"description":"OK"}},"description":"Delete a short link","security":[{"bearerAuth":[]}],"requestBody":{"required":true,"content":{"application/json":{"schema":{"type":"object","required":["slug"],"properties":{"slug":{"type":"string","description":"The slug of the link to delete"}}}}}}}},"/api/link/edit":{"put":{"tags":["API Routes"],"parameters":[],"responses":{"200":{"description":"OK"}},"description":"Edit an existing short link","security":[{"bearerAuth":[]}],"requestBody":{"required":true,"content":{"application/json":{"schema":{"$ref":"#/components/schemas/EditLink"}}}}}},"/api/link/export":{"get":{"tags":["API Routes"],"parameters":[{"name":"cursor","in":"query","required":false,"schema":{"type":"string"},"description":"Pagination cursor from previous response"},{"name":"status","in":"query","required":false,"schema":{"type":"string","enum":["active","expired","all"],"default":"all"},"description":"Expiration status filter"}],"responses":{"200":{"description":"OK"}},"description":"Export all links with pagination","security":[{"bearerAuth":[]}]}},"/api/link/history":{"get":{"tags":["API Routes"],"parameters":[],"responses":{"200":{"description":"OK"}}}},"/api/link/import":{"post":{"tags":["API Routes"],"parameters":[],"responses":{"200":{"description":"OK"}},"description":"Import links from exported data","security":[{"bearerAuth":[]}],"requestBody":{"required":true,"content":{"application/json":{"schema":{"$ref":"#/components/schemas/ImportData"}}}}}},"/api/link/list":{"get":{"tags":["API Routes"],"parameters":[{"name":"limit","in":"query","required":false,"schema":{"type":"integer","default":20,"maximum":1000},"description":"Maximum number of links to return"},{"name":"cursor","in":"query","required":false,"schema":{"type":"string"},"description":"Pagination cursor from previous response"},{"name":"sort","in":"query","required":false,"schema":{"type":"string","enum":["az","za","newest","oldest"],"default":"newest"},"description":"Link sort order"},{"name":"tag","in":"query","required":false,"schema":{"type":"string"},"description":"Exact normalized tag filter"},{"name":"status","in":"query","required":false,"schema":{"type":"string","enum":["active","expired","all"],"default":"active"},"description":"Expiration status filter"}],"responses":{"200":{"description":"OK"}},"description":"List all short links with pagination","security":[{"bearerAuth":[]}]}},"/api/link/migration/run":{"post":{"tags":["API Routes"],"parameters":[{"name":"cursor","in":"query","required":false,"schema":{"type":"string"},"description":"Opaque migration run cursor"},{"name":"force","in":"query","required":false,"schema":{"type":"boolean","default":false},"description":"Rescan KV without overwriting D1 rows"}],"responses":{"200":{"description":"OK"}},"description":"Migrate one bounded page of legacy KV links to D1","security":[{"bearerAuth":[]}],"requestBody":{"required":false,"content":{"application/json":{"schema":{"type":"object","properties":{"cursor":{"type":"string","description":"Opaque migration run cursor"},"force":{"type":"boolean","default":false,"description":"Rescan KV without overwriting D1 rows"}}}}}}}},"/api/link/migration/status":{"get":{"tags":["API Routes"],"parameters":[],"responses":{"200":{"description":"OK"}},"description":"Get KV to D1 link migration status","security":[{"bearerAuth":[]}]}},"/api/link/monitor":{"put":{"tags":["API Routes"],"parameters":[],"responses":{"200":{"description":"OK"}}}},"/api/link/monitors":{"get":{"tags":["API Routes"],"parameters":[],"responses":{"200":{"description":"OK"}}}},"/api/link/og-ai":{"get":{"tags":["API Routes"],"parameters":[{"name":"url","in":"query","required":true,"schema":{"type":"string","format":"uri"},"description":"The URL to generate OpenGraph metadata for"},{"name":"locale","in":"query","required":false,"schema":{"type":"string"},"description":"Preferred locale for the generated metadata"}],"responses":{"200":{"description":"OK"}},"description":"Generate OpenGraph title and description using AI based on the URL","security":[{"bearerAuth":[]}]}},"/api/link/preview":{"post":{"tags":["API Routes"],"parameters":[],"responses":{"200":{"description":"OK"}}}},"/api/link/query":{"get":{"tags":["API Routes"],"parameters":[{"name":"slug","in":"query","required":true,"schema":{"type":"string"},"description":"The slug of the link to query"}],"responses":{"200":{"description":"OK"}},"description":"Query a short link by slug","security":[{"bearerAuth":[]}]}},"/api/link/search":{"get":{"tags":["API Routes"],"parameters":[{"name":"q","in":"query","required":false,"schema":{"type":"string"},"description":"Non-empty case-insensitive substring to match against slug, URL, comment, or tag"},{"name":"url","in":"query","required":false,"schema":{"type":"string"},"description":"Normalized target URL to match exactly"},{"name":"tag","in":"query","required":false,"schema":{"type":"string"},"description":"Exact normalized tag filter"},{"name":"status","in":"query","required":false,"schema":{"type":"string","enum":["active","expired","all"],"default":"active"},"description":"Expiration status filter"},{"name":"limit","in":"query","required":false,"schema":{"type":"integer","minimum":1,"maximum":1000,"default":20},"description":"Maximum matches for keyword or exact URL searches"}],"responses":{"200":{"description":"OK"}},"description":"Search links with a non-empty keyword or exact URL. Tag and status only filter those searches; requests without a search selector return an empty array.","security":[{"bearerAuth":[]}]},"post":{"tags":["API Routes"],"parameters":[],"responses":{"200":{"description":"OK"}},"description":"Search links by an exact target URL without placing the URL in the request query string","security":[{"bearerAuth":[]}],"requestBody":{"required":true,"content":{"application/json":{"schema":{"$ref":"#/components/schemas/ExactUrlSearch"}}}}}},"/api/link/suggest":{"post":{"tags":["API Routes"],"parameters":[],"responses":{"200":{"description":"OK"}},"description":"Suggest available memorable link endings without AI fees. Optional metadata reads a bounded public HTTPS page title.","security":[{"bearerAuth":[]}],"requestBody":{"required":true,"content":{"application/json":{"schema":{"$ref":"#/components/schemas/WordSuggestions"}}}}}},"/api/link/tags":{"get":{"tags":["API Routes"],"parameters":[],"responses":{"200":{"description":"OK"}},"description":"List tags currently used by links","security":[{"bearerAuth":[]}]}},"/api/link/upsert":{"post":{"tags":["API Routes"],"parameters":[],"responses":{"200":{"description":"OK"}},"description":"Create a short link, or return the existing one when the slug is taken","security":[{"bearerAuth":[]}],"requestBody":{"required":true,"content":{"application/json":{"schema":{"$ref":"#/components/schemas/CreateLink"}}}}}},"/api/location":{"get":{"tags":["API Routes"],"parameters":[],"responses":{"200":{"description":"The location of the user"}},"description":"Get the location of the user"}},"/api/logs/events":{"get":{"tags":["API Routes"],"parameters":[],"responses":{"200":{"description":"OK"}}}},"/api/logs/locations":{"get":{"tags":["API Routes"],"parameters":[],"responses":{"200":{"description":"OK"}}}},"/api/mcp":{"get":{"tags":["MCP"],"parameters":[],"responses":{"200":{"description":"OK"}},"description":"Model Context Protocol endpoint (Streamable HTTP, stateless) served by the official @modelcontextprotocol/server SDK v2. Accepts standard MCP messages such as initialize, tools/list, tools/call, and ping. A message without an `id` is a notification and is answered with 202.","security":[{"bearerAuth":[]}],"requestBody":{"required":true,"content":{"application/json":{"schema":{"type":"object","required":["jsonrpc","method"],"additionalProperties":true,"description":"`method` is one of initialize, notifications/initialized, tools/list, tools/call, or ping."}}}}}},"/api/public/availability":{"get":{"tags":["API Routes"],"parameters":[],"responses":{"200":{"description":"OK"}}}},"/api/stats/{action}":{"get":{"tags":["API Routes"],"parameters":[{"name":"action","in":"path","required":true,"schema":{"type":"string"}}],"responses":{"200":{"description":"OK"}}}},"/api/stats/counters":{"get":{"tags":["API Routes"],"parameters":[],"responses":{"200":{"description":"OK"}}}},"/api/stats/heatmap":{"get":{"tags":["API Routes"],"parameters":[],"responses":{"200":{"description":"OK"}}}},"/api/stats/metrics":{"get":{"tags":["API Routes"],"parameters":[],"responses":{"200":{"description":"OK"}}}},"/api/stats/views":{"get":{"tags":["API Routes"],"parameters":[],"responses":{"200":{"description":"OK"}}}},"/api/upload/image":{"post":{"tags":["API Routes"],"parameters":[],"responses":{"200":{"description":"OK"}},"description":"Upload an image to R2 storage","requestBody":{"required":true,"content":{"multipart/form-data":{"schema":{"type":"object","required":["file","slug"],"properties":{"file":{"type":"string","format":"binary"},"slug":{"type":"string"}}}}}}}},"/api/verify":{"get":{"tags":["API Routes"],"parameters":[],"responses":{"200":{"description":"The authentication credentials are valid"},"default":{"description":"The authentication credentials are invalid"}},"description":"Verify the current authentication method"}},"/_assets/{key}":{"get":{"tags":["Internal"],"parameters":[{"name":"key","in":"path","required":true,"schema":{"type":"string"}}],"responses":{"200":{"description":"OK"}}}},"/_health":{"get":{"tags":["Internal"],"parameters":[],"responses":{"200":{"description":"OK"}}}},"/__nuxt_error":{"get":{"tags":["Internal"],"parameters":[],"responses":{"200":{"description":"OK"}}}},"/_i18n/{hash}/{locale}/messages.json":{"get":{"tags":["Internal"],"parameters":[{"name":"hash","in":"path","required":true,"schema":{"type":"string"}},{"name":"locale","in":"path","required":true,"schema":{"type":"string"}}],"responses":{"200":{"description":"OK"}}}},"/__nuxt_island/{*param1}":{"get":{"tags":["Internal"],"parameters":[{"name":"*param1","in":"path","required":true,"schema":{"type":"string"}}],"responses":{"200":{"description":"OK"}}}},"/_docs/openapi.json":{"get":{"tags":["Internal"],"parameters":[],"responses":{"200":{"description":"OK"}}}},"/_docs/scalar":{"get":{"tags":["Internal"],"parameters":[],"responses":{"200":{"description":"OK"}}}},"/_docs/swagger":{"get":{"tags":["Internal"],"parameters":[],"responses":{"200":{"description":"OK"}}}}},"components":{"securitySchemes":{"bearerAuth":{"type":"http","scheme":"bearer","description":"Use a Snappy API key as the bearer token. Create and revoke keys in your account."}},"schemas":{"CreateLink":{"$schema":"https://json-schema.org/draft/2020-12/schema","type":"object","properties":{"url":{"type":"string","minLength":1,"maxLength":16384,"format":"uri","description":"The target URL."},"slug":{"type":"string","maxLength":2048,"pattern":"^[a-z0-9]+(?:-[a-z0-9]+)*(?:\\/[a-z0-9]+(?:-[a-z0-9]+)*)?$"},"comment":{"description":"Optional internal note.","type":"string","maxLength":2048},"expiration":{"description":"Unix seconds. Must be in the future.","type":"integer","minimum":-9007199254740991,"maximum":9007199254740991},"title":{"description":"Overrides the title in the link preview.","type":"string","maxLength":256},"description":{"description":"Overrides the description in the link preview.","type":"string","maxLength":2048},"image":{"description":"Overrides the image in the link preview.","type":"string","maxLength":128},"apple":{"description":"Apple App Store redirect URL.","type":"string","minLength":1,"maxLength":16384,"format":"uri"},"google":{"description":"Google Play Store redirect URL.","type":"string","minLength":1,"maxLength":16384,"format":"uri"},"cloaking":{"description":"Mask the destination URL behind the short link.","type":"boolean"},"redirectWithQuery":{"description":"Append incoming query parameters to the destination URL.","type":"boolean"},"proxy":{"description":"Reverse-proxy the destination instead of redirecting.","type":"boolean"},"password":{"type":"string","minLength":1,"maxLength":128,"description":"Password protection for the link."},"unsafe":{"description":"Show a warning page before redirecting.","type":"boolean"},"geo":{"description":"Geo-routing rules mapping a two-letter country code to a URL.","type":"object","propertyNames":{"type":"string","pattern":"^[A-Z]{2}$"},"additionalProperties":{"type":"string","maxLength":16384,"format":"uri"}},"tags":{"description":"Up to 10 normalized link tags, each 1-32 characters.","maxItems":10,"type":"array","items":{"type":"string","minLength":1,"maxLength":32}},"id":{"type":"string","minLength":1,"maxLength":26},"createdAt":{"type":"integer","minimum":-9007199254740991,"maximum":9007199254740991},"updatedAt":{"type":"integer","minimum":-9007199254740991,"maximum":9007199254740991}},"required":["url"]},"EditLink":{"$schema":"https://json-schema.org/draft/2020-12/schema","type":"object","properties":{"url":{"type":"string","minLength":1,"maxLength":16384,"format":"uri","description":"The target URL."},"slug":{"type":"string","maxLength":2048,"pattern":"^[a-z0-9]+(?:-[a-z0-9]+)*(?:\\/[a-z0-9]+(?:-[a-z0-9]+)*)?$","description":"The slug identifying the short link."},"comment":{"description":"Optional internal note.","type":"string","maxLength":2048},"expiration":{"description":"Unix seconds. Must be in the future.","type":"integer","minimum":-9007199254740991,"maximum":9007199254740991},"title":{"description":"Overrides the title in the link preview.","type":"string","maxLength":256},"description":{"description":"Overrides the description in the link preview.","type":"string","maxLength":2048},"image":{"description":"Overrides the image in the link preview.","type":"string","maxLength":128},"apple":{"description":"Apple App Store redirect URL.","type":"string","minLength":1,"maxLength":16384,"format":"uri"},"google":{"description":"Google Play Store redirect URL.","type":"string","minLength":1,"maxLength":16384,"format":"uri"},"cloaking":{"description":"Mask the destination URL behind the short link.","type":"boolean"},"redirectWithQuery":{"description":"Append incoming query parameters to the destination URL.","type":"boolean"},"proxy":{"description":"Reverse-proxy the destination instead of redirecting.","type":"boolean"},"password":{"description":"Password protection for the link. An empty string clears it; omitting keeps the stored one.","type":"string","maxLength":128},"unsafe":{"description":"Show a warning page before redirecting.","type":"boolean"},"geo":{"description":"Geo-routing rules mapping a two-letter country code to a URL.","type":"object","propertyNames":{"type":"string","pattern":"^[A-Z]{2}$"},"additionalProperties":{"type":"string","maxLength":16384,"format":"uri"}},"tags":{"description":"Up to 10 normalized link tags, each 1-32 characters.","maxItems":10,"type":"array","items":{"type":"string","minLength":1,"maxLength":32}},"expectedVersion":{"description":"Version returned by get_link or preview. Reject the edit if the destination changed since review.","type":"integer","minimum":-9007199254740991,"maximum":9007199254740991}},"required":["url","slug"]},"ImportData":{"$schema":"https://json-schema.org/draft/2020-12/schema","type":"object","properties":{"version":{"type":"string"},"exportedAt":{"type":"string"},"count":{"type":"integer","minimum":-9007199254740991,"maximum":9007199254740991},"links":{"minItems":1,"type":"array","items":{"type":"object","properties":{"url":{"type":"string","minLength":1,"maxLength":16384,"format":"uri","description":"The target URL."},"slug":{"type":"string","maxLength":2048,"pattern":"^[a-z0-9]+(?:-[a-z0-9]+)*(?:\\/[a-z0-9]+(?:-[a-z0-9]+)*)?$"},"comment":{"description":"Optional internal note.","type":"string","maxLength":2048},"expiration":{"type":"integer","minimum":-9007199254740991,"maximum":9007199254740991},"title":{"description":"Overrides the title in the link preview.","type":"string","maxLength":256},"description":{"description":"Overrides the description in the link preview.","type":"string","maxLength":2048},"image":{"description":"Overrides the image in the link preview.","type":"string","maxLength":128},"apple":{"description":"Apple App Store redirect URL.","type":"string","minLength":1,"maxLength":16384,"format":"uri"},"google":{"description":"Google Play Store redirect URL.","type":"string","minLength":1,"maxLength":16384,"format":"uri"},"cloaking":{"description":"Mask the destination URL behind the short link.","type":"boolean"},"redirectWithQuery":{"description":"Append incoming query parameters to the destination URL.","type":"boolean"},"proxy":{"description":"Reverse-proxy the destination instead of redirecting.","type":"boolean"},"password":{"type":"string","minLength":1,"maxLength":128,"description":"Password protection for the link."},"unsafe":{"description":"Show a warning page before redirecting.","type":"boolean"},"geo":{"description":"Geo-routing rules mapping a two-letter country code to a URL.","type":"object","propertyNames":{"type":"string","pattern":"^[A-Z]{2}$"},"additionalProperties":{"type":"string","maxLength":16384,"format":"uri"}},"tags":{"description":"Up to 10 normalized link tags, each 1-32 characters.","maxItems":10,"type":"array","items":{"type":"string","minLength":1,"maxLength":32}},"id":{"type":"string","minLength":1,"maxLength":26},"createdAt":{"type":"integer","minimum":-9007199254740991,"maximum":9007199254740991},"updatedAt":{"type":"integer","minimum":-9007199254740991,"maximum":9007199254740991}},"required":["url","slug"]}}},"required":["version","links"]},"ExactUrlSearch":{"$schema":"https://json-schema.org/draft/2020-12/schema","type":"object","properties":{"url":{"type":"string","minLength":1,"maxLength":16384,"format":"uri","description":"Normalized target URL to match exactly."},"limit":{"default":20,"description":"Maximum number of results, 1-1000.","type":"integer","minimum":1,"maximum":1000}},"required":["url"]},"WordSuggestions":{"$schema":"https://json-schema.org/draft/2020-12/schema","type":"object","properties":{"url":{"type":"string","minLength":1,"maxLength":16384,"format":"uri"},"mode":{"default":"personal","type":"string","enum":["personal","global"]},"metadata":{"default":false,"type":"boolean"},"offset":{"default":0,"type":"integer","minimum":0,"maximum":96}},"required":["url"]}}}}