{"openapi":"3.1.0","info":{"title":"Whooshly API","version":"1.0.0","description":"Create and manage campaign links, QR codes, UTM templates, vCards and hosted landing pages, and read analytics. Authenticated with a Bearer token (mint one at https://app.whooshly.co/agents) or an OAuth access token. The same operations are available over MCP at https://app.whooshly.co/api/mcp."},"servers":[{"url":"https://app.whooshly.co"}],"security":[{"bearerAuth":[]}],"components":{"securitySchemes":{"bearerAuth":{"type":"http","scheme":"bearer"}},"schemas":{"Link":{"type":"object","properties":{"id":{"type":"string"},"title":{"type":["string","null"]},"tags":{"type":"array","maxItems":20,"items":{"type":"string","maxLength":40}},"slug":{"type":"string"},"destination":{"type":"string","format":"uri","description":"http(s) URL"},"type":{"type":"string"},"active":{"type":"boolean"},"archivedAt":{"type":["string","null"],"format":"date-time"}}},"QrStyle":{"type":"object","properties":{"fg":{"type":"string"},"bg":{"type":"string"},"margin":{"type":"integer"}}}}},"paths":{"/api/links":{"get":{"summary":"List links (newest first, cursor-paginated)","parameters":[{"name":"cursor","in":"query","schema":{"type":"string"}},{"name":"limit","in":"query","schema":{"type":"integer","maximum":100}},{"name":"archived","in":"query","schema":{"type":"string","enum":["only","all"]}},{"name":"q","in":"query","schema":{"type":"string"}},{"name":"type","in":"query","schema":{"type":"string","enum":["short","qr","vcard","bio","page"]}},{"name":"tag","in":"query","schema":{"type":"string"}},{"name":"active","in":"query","schema":{"type":"boolean"}},{"name":"createdAfter","in":"query","schema":{"type":"integer","description":"Epoch milliseconds"}},{"name":"createdBefore","in":"query","schema":{"type":"integer","description":"Epoch milliseconds"}}],"responses":{"200":{"description":"Links","content":{"application/json":{"schema":{"type":"object","properties":{"links":{"type":"array","items":{"$ref":"#/components/schemas/Link"}},"nextCursor":{"type":["string","null"]}}}}}},"401":{"description":"Unauthorized"}}},"post":{"summary":"Create a short link","requestBody":{"required":true,"content":{"application/json":{"schema":{"type":"object","required":["destination"],"properties":{"destination":{"type":"string","format":"uri","description":"http(s) URL"},"slug":{"type":"string","maxLength":64},"title":{"type":"string","maxLength":160},"tags":{"type":"array","maxItems":20,"items":{"type":"string","maxLength":40}}}}}}},"responses":{"201":{"description":"Created","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Link"}}}},"400":{"description":"Error — `error` is a stable code. Common: core_locked (account hasn't purchased — buy at the app), pro_required (feature needs Pro), slug_taken, limit_reached, not_found, rate_limited, invalid (see issues).","content":{"application/json":{"schema":{"type":"object","properties":{"error":{"type":"string"},"issues":{"type":"array"}}}}}},"402":{"description":"Error — `error` is a stable code. Common: core_locked (account hasn't purchased — buy at the app), pro_required (feature needs Pro), slug_taken, limit_reached, not_found, rate_limited, invalid (see issues).","content":{"application/json":{"schema":{"type":"object","properties":{"error":{"type":"string"},"issues":{"type":"array"}}}}}},"409":{"description":"Error — `error` is a stable code. Common: core_locked (account hasn't purchased — buy at the app), pro_required (feature needs Pro), slug_taken, limit_reached, not_found, rate_limited, invalid (see issues).","content":{"application/json":{"schema":{"type":"object","properties":{"error":{"type":"string"},"issues":{"type":"array"}}}}}}}}},"/api/links/{id}":{"get":{"summary":"Get one link","parameters":[{"name":"id","in":"path","required":true,"schema":{"type":"string"}}],"responses":{"200":{"description":"Link","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Link"}}}},"404":{"description":"Error — `error` is a stable code. Common: core_locked (account hasn't purchased — buy at the app), pro_required (feature needs Pro), slug_taken, limit_reached, not_found, rate_limited, invalid (see issues).","content":{"application/json":{"schema":{"type":"object","properties":{"error":{"type":"string"},"issues":{"type":"array"}}}}}}}},"patch":{"summary":"Edit, tag, archive, or restore a link","parameters":[{"name":"id","in":"path","required":true,"schema":{"type":"string"}}],"requestBody":{"required":true,"content":{"application/json":{"schema":{"type":"object","properties":{"destination":{"type":"string","format":"uri","description":"http(s) URL"},"slug":{"type":"string","maxLength":64},"title":{"type":["string","null"],"maxLength":160},"tags":{"type":"array","maxItems":20,"items":{"type":"string","maxLength":40}},"active":{"type":"boolean"},"archived":{"type":"boolean"}}}}}},"responses":{"200":{"description":"Updated"},"404":{"description":"Error — `error` is a stable code. Common: core_locked (account hasn't purchased — buy at the app), pro_required (feature needs Pro), slug_taken, limit_reached, not_found, rate_limited, invalid (see issues).","content":{"application/json":{"schema":{"type":"object","properties":{"error":{"type":"string"},"issues":{"type":"array"}}}}}}}}},"/api/links/{id}/stats":{"get":{"summary":"Exact click/scan totals + daily series","parameters":[{"name":"id","in":"path","required":true,"schema":{"type":"string"}}],"responses":{"200":{"description":"Stats"},"404":{"description":"Error — `error` is a stable code. Common: core_locked (account hasn't purchased — buy at the app), pro_required (feature needs Pro), slug_taken, limit_reached, not_found, rate_limited, invalid (see issues).","content":{"application/json":{"schema":{"type":"object","properties":{"error":{"type":"string"},"issues":{"type":"array"}}}}}}}}},"/api/qr":{"get":{"summary":"Search and filter dynamic QR codes","parameters":[{"name":"q","in":"query","schema":{"type":"string"}},{"name":"tag","in":"query","schema":{"type":"string"}},{"name":"active","in":"query","schema":{"type":"boolean"}},{"name":"createdAfter","in":"query","schema":{"type":"integer"}},{"name":"createdBefore","in":"query","schema":{"type":"integer"}}],"responses":{"200":{"description":"QR codes"}}},"post":{"summary":"Create a dynamic QR code","requestBody":{"required":true,"content":{"application/json":{"schema":{"type":"object","required":["destination"],"properties":{"destination":{"type":"string","format":"uri","description":"http(s) URL"},"slug":{"type":"string"},"title":{"type":"string","maxLength":160},"tags":{"type":"array","maxItems":20,"items":{"type":"string","maxLength":40}},"style":{"$ref":"#/components/schemas/QrStyle"}}}}}},"responses":{"201":{"description":"Created"},"402":{"description":"Error — `error` is a stable code. Common: core_locked (account hasn't purchased — buy at the app), pro_required (feature needs Pro), slug_taken, limit_reached, not_found, rate_limited, invalid (see issues).","content":{"application/json":{"schema":{"type":"object","properties":{"error":{"type":"string"},"issues":{"type":"array"}}}}}}}}},"/api/qr/{id}":{"get":{"summary":"Get one QR code (style + destination)","parameters":[{"name":"id","in":"path","required":true,"schema":{"type":"string"}}],"responses":{"200":{"description":"QR"},"404":{"description":"Error — `error` is a stable code. Common: core_locked (account hasn't purchased — buy at the app), pro_required (feature needs Pro), slug_taken, limit_reached, not_found, rate_limited, invalid (see issues).","content":{"application/json":{"schema":{"type":"object","properties":{"error":{"type":"string"},"issues":{"type":"array"}}}}}}}}},"/api/utm":{"get":{"summary":"List UTM templates","responses":{"200":{"description":"Templates"}}},"post":{"summary":"Save a UTM template","requestBody":{"required":true,"content":{"application/json":{"schema":{"type":"object","required":["name","params"],"properties":{"name":{"type":"string"},"params":{"type":"object"}}}}}},"responses":{"201":{"description":"Created"}}}},"/api/vcard":{"post":{"summary":"Create a vCard page","requestBody":{"required":true,"content":{"application/json":{"schema":{"type":"object","required":["fields"],"properties":{"fields":{"type":"object"},"slug":{"type":"string"}}}}}},"responses":{"201":{"description":"Created"}}}},"/api/pages":{"get":{"summary":"List landing pages (with lead counts)","responses":{"200":{"description":"Pages"}}},"post":{"summary":"Create a landing page on the account's subdomain (claim the subdomain in the app first)","requestBody":{"required":true,"content":{"application/json":{"schema":{"type":"object","required":["fields"],"properties":{"fields":{"type":"object","description":"title + blocks (hero/text/image/button/divider/form)"},"slug":{"type":"string","maxLength":64},"theme":{"type":"object"}}}}}},"responses":{"201":{"description":"Created"},"402":{"description":"Error — `error` is a stable code. Common: core_locked (account hasn't purchased — buy at the app), pro_required (feature needs Pro), slug_taken, limit_reached, not_found, rate_limited, invalid (see issues).","content":{"application/json":{"schema":{"type":"object","properties":{"error":{"type":"string"},"issues":{"type":"array"}}}}}},"409":{"description":"Error — `error` is a stable code. Common: core_locked (account hasn't purchased — buy at the app), pro_required (feature needs Pro), slug_taken, limit_reached, not_found, rate_limited, invalid (see issues).","content":{"application/json":{"schema":{"type":"object","properties":{"error":{"type":"string"},"issues":{"type":"array"}}}}}},"422":{"description":"Error — `error` is a stable code. Common: core_locked (account hasn't purchased — buy at the app), pro_required (feature needs Pro), slug_taken, limit_reached, not_found, rate_limited, invalid (see issues).","content":{"application/json":{"schema":{"type":"object","properties":{"error":{"type":"string"},"issues":{"type":"array"}}}}}}}}},"/api/pages/{id}":{"get":{"summary":"Get one landing page (content, lead count, webhook delivery rollup)","parameters":[{"name":"id","in":"path","required":true,"schema":{"type":"string"}}],"responses":{"200":{"description":"Page"},"404":{"description":"Error — `error` is a stable code. Common: core_locked (account hasn't purchased — buy at the app), pro_required (feature needs Pro), slug_taken, limit_reached, not_found, rate_limited, invalid (see issues).","content":{"application/json":{"schema":{"type":"object","properties":{"error":{"type":"string"},"issues":{"type":"array"}}}}}}}},"patch":{"summary":"Update a landing page (blocks, theme, slug) — edits never delete anything","parameters":[{"name":"id","in":"path","required":true,"schema":{"type":"string"}}],"requestBody":{"required":true,"content":{"application/json":{"schema":{"type":"object","properties":{"fields":{"type":"object"},"slug":{"type":"string"},"theme":{"type":"object"}}}}}},"responses":{"200":{"description":"Updated"},"404":{"description":"Error — `error` is a stable code. Common: core_locked (account hasn't purchased — buy at the app), pro_required (feature needs Pro), slug_taken, limit_reached, not_found, rate_limited, invalid (see issues).","content":{"application/json":{"schema":{"type":"object","properties":{"error":{"type":"string"},"issues":{"type":"array"}}}}}},"409":{"description":"Error — `error` is a stable code. Common: core_locked (account hasn't purchased — buy at the app), pro_required (feature needs Pro), slug_taken, limit_reached, not_found, rate_limited, invalid (see issues).","content":{"application/json":{"schema":{"type":"object","properties":{"error":{"type":"string"},"issues":{"type":"array"}}}}}}}}}}}