{"openapi":"3.1.0","info":{"title":"Tapter Profile API","version":"1.0.0","description":"Read and edit Tapter profiles (links) and their content blocks (slots). Strict on write, tolerant on read: unknown fields are rejected with 422, legacy slots are returned with \"supported\": false."},"servers":[{"url":"https://tapter.one/api/v1"}],"tags":[{"name":"Profiles"},{"name":"Slots"},{"name":"Discovery"},{"name":"Music"},{"name":"Files"}],"security":[{"bearerAuth":[]}],"paths":{"/profiles":{"get":{"operationId":"listProfiles","summary":"List the profiles of the token owner.","tags":["Profiles"],"responses":{"200":{"description":"","content":{"application/json":{"schema":{"type":"object","properties":{"data":{"type":"array","items":{"$ref":"#/components/schemas/ProfileResource"}},"meta":{"type":"object","properties":{"total":{"type":"integer"}},"required":["total"]}},"required":["data","meta"]}}}},"401":{"$ref":"#/components/responses/AuthenticationException"},"429":{"description":"Rate limit exceeded. See Retry-After.","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"}}}}}},"post":{"operationId":"createProfile","summary":"Create a new link (profile).","tags":["Profiles"],"parameters":[{"name":"Idempotency-Key","in":"header","description":"Repeating the same key within 24 hours returns the stored response and creates nothing. A different body with the same key returns 422.","schema":{"type":"string","maxLength":128}},{"name":"Precognition","in":"header","description":"Set to \"true\" for a dry run: the request is validated and not executed (204 on success, 422 on failure).","schema":{"type":"string","enum":["true"]}}],"requestBody":{"required":true,"content":{"application/json":{"schema":{"$ref":"#/components/schemas/StoreProfileRequest"}}}},"responses":{"201":{"description":"","content":{"application/json":{"schema":{"type":"object","properties":{"data":{"$ref":"#/components/schemas/ProfileResource"}},"required":["data"]}}}},"401":{"$ref":"#/components/responses/AuthenticationException"},"422":{"$ref":"#/components/responses/ValidationException"},"403":{"$ref":"#/components/responses/AuthorizationException"},"429":{"description":"Rate limit exceeded. See Retry-After.","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"}}}},"409":{"description":"A business rule was violated: limits, or writing to a read-only slot type.","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"}}}}}}},"/profiles/check-login":{"get":{"operationId":"checkLogin","summary":"Check whether a login is available.","tags":["Profiles"],"parameters":[{"name":"login","in":"query","required":true,"description":"The login to check.","schema":{"type":"string","maxLength":255}},{"name":"profile","in":"query","description":"Id or login of one of your own profiles. Its current login is then not reported as taken —\nuse it when checking a login before changing it.","schema":{"type":["string","null"],"maxLength":255}}],"responses":{"200":{"description":"","content":{"application/json":{"schema":{"type":"object","properties":{"data":{"type":"object","properties":{"login":{"type":"string"},"profile":{"type":["string","null"]},"available":{"type":"boolean"},"reason":{"type":["string","null"]},"message":{"type":"string"}},"required":["login","profile","available","reason","message"]}},"required":["data"]}}}},"401":{"$ref":"#/components/responses/AuthenticationException"},"422":{"$ref":"#/components/responses/ValidationException"},"403":{"$ref":"#/components/responses/AuthorizationException"},"429":{"description":"Rate limit exceeded. See Retry-After.","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"}}}}}}},"/profiles/{profileKey}":{"get":{"operationId":"getProfile","summary":"Get one profile.","tags":["Profiles"],"parameters":[{"name":"profileKey","in":"path","required":true,"description":"Numeric profile id or login. A fully numeric segment is always treated as an id.","schema":{"type":"string"}}],"responses":{"200":{"description":"","content":{"application/json":{"schema":{"type":"object","properties":{"data":{"$ref":"#/components/schemas/ProfileResource"}},"required":["data"]}}}},"404":{"$ref":"#/components/responses/ModelNotFoundException"},"401":{"$ref":"#/components/responses/AuthenticationException"},"422":{"$ref":"#/components/responses/ValidationException"},"403":{"$ref":"#/components/responses/AuthorizationException"},"429":{"description":"Rate limit exceeded. See Retry-After.","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"}}}}}},"patch":{"operationId":"updateProfile","summary":"Partially update a profile. Omitted fields are left untouched.","tags":["Profiles"],"parameters":[{"name":"profileKey","in":"path","required":true,"description":"Numeric profile id or login. A fully numeric segment is always treated as an id.","schema":{"type":"string"}},{"name":"Precognition","in":"header","description":"Set to \"true\" for a dry run: the request is validated and not executed (204 on success, 422 on failure).","schema":{"type":"string","enum":["true"]}}],"requestBody":{"content":{"application/json":{"schema":{"$ref":"#/components/schemas/UpdateProfileRequest"}}}},"responses":{"200":{"description":"","content":{"application/json":{"schema":{"type":"object","properties":{"data":{"$ref":"#/components/schemas/ProfileResource"}},"required":["data"]}}}},"404":{"$ref":"#/components/responses/ModelNotFoundException"},"401":{"$ref":"#/components/responses/AuthenticationException"},"422":{"$ref":"#/components/responses/ValidationException"},"403":{"$ref":"#/components/responses/AuthorizationException"},"429":{"description":"Rate limit exceeded. See Retry-After.","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"}}}},"409":{"description":"A business rule was violated: limits, or writing to a read-only slot type.","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"}}}}}}},"/profiles/{profileKey}/settings":{"get":{"operationId":"getProfileSettings","summary":"Get profile settings together with the values they accept: the active languages and the login rules.","tags":["Profiles"],"parameters":[{"name":"profileKey","in":"path","required":true,"description":"Numeric profile id or login. A fully numeric segment is always treated as an id.","schema":{"type":"string"}}],"responses":{"200":{"description":"","content":{"application/json":{"schema":{"type":"object","properties":{"data":{"$ref":"#/components/schemas/ProfileSettingsResource"}},"required":["data"]}}}},"404":{"$ref":"#/components/responses/ModelNotFoundException"},"401":{"$ref":"#/components/responses/AuthenticationException"},"422":{"$ref":"#/components/responses/ValidationException"},"403":{"$ref":"#/components/responses/AuthorizationException"},"429":{"description":"Rate limit exceeded. See Retry-After.","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"}}}}}},"patch":{"operationId":"updateProfileSettings","summary":"Change the login or the language of the page. Omitted fields are left untouched.","tags":["Profiles"],"parameters":[{"name":"profileKey","in":"path","required":true,"description":"Numeric profile id or login. A fully numeric segment is always treated as an id.","schema":{"type":"string"}},{"name":"Precognition","in":"header","description":"Set to \"true\" for a dry run: the request is validated and not executed (204 on success, 422 on failure).","schema":{"type":"string","enum":["true"]}}],"requestBody":{"content":{"application/json":{"schema":{"$ref":"#/components/schemas/UpdateSettingsRequest"}}}},"responses":{"200":{"description":"","content":{"application/json":{"schema":{"type":"object","properties":{"data":{"$ref":"#/components/schemas/ProfileSettingsResource"}},"required":["data"]}}}},"404":{"$ref":"#/components/responses/ModelNotFoundException"},"401":{"$ref":"#/components/responses/AuthenticationException"},"422":{"$ref":"#/components/responses/ValidationException"},"403":{"$ref":"#/components/responses/AuthorizationException"},"429":{"description":"Rate limit exceeded. See Retry-After.","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"}}}},"409":{"description":"A business rule was violated: limits, or writing to a read-only slot type.","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"}}}}}}},"/profiles/{profileKey}/markdown":{"get":{"operationId":"previewProfileMarkdown","description":"Use this to check your own work after creating or changing slots: it shows the real order, the real texts and which blocks came out empty. Returns text/markdown, not JSON.","summary":"Render the whole profile as markdown — the page as a language model sees it.","tags":["Profiles"],"parameters":[{"name":"profileKey","in":"path","required":true,"description":"Numeric profile id or login. A fully numeric segment is always treated as an id.","schema":{"type":"string"}}],"responses":{"200":{"description":"The rendered page.","content":{"text/markdown":{"schema":{"type":"string"}}},"headers":{"x-markdown-tokens":{"schema":{"type":"integer"}}}},"404":{"$ref":"#/components/responses/ModelNotFoundException"},"401":{"$ref":"#/components/responses/AuthenticationException"},"422":{"$ref":"#/components/responses/ValidationException"},"403":{"$ref":"#/components/responses/AuthorizationException"},"429":{"description":"Rate limit exceeded. See Retry-After.","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"}}}}}}},"/profiles/{profileKey}/slots":{"get":{"operationId":"listSlots","summary":"List the slots of a profile, ordered.","tags":["Slots"],"parameters":[{"name":"profileKey","in":"path","required":true,"description":"Numeric profile id or login. A fully numeric segment is always treated as an id.","schema":{"type":"string"}}],"responses":{"200":{"description":"","content":{"application/json":{"schema":{"type":"object","properties":{"data":{"type":"array","items":{"$ref":"#/components/schemas/SlotResource"}}},"required":["data"]}}}},"404":{"$ref":"#/components/responses/ModelNotFoundException"},"401":{"$ref":"#/components/responses/AuthenticationException"},"422":{"$ref":"#/components/responses/ValidationException"},"403":{"$ref":"#/components/responses/AuthorizationException"},"429":{"description":"Rate limit exceeded. See Retry-After.","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"}}}}}},"post":{"operationId":"createSlot","summary":"Create a slot. Without order it goes to the end.","tags":["Slots"],"parameters":[{"name":"profileKey","in":"path","required":true,"description":"Numeric profile id or login. A fully numeric segment is always treated as an id.","schema":{"type":"string"}},{"name":"Idempotency-Key","in":"header","description":"Repeating the same key within 24 hours returns the stored response and creates nothing. A different body with the same key returns 422.","schema":{"type":"string","maxLength":128}},{"name":"Precognition","in":"header","description":"Set to \"true\" for a dry run: the request is validated and not executed (204 on success, 422 on failure).","schema":{"type":"string","enum":["true"]}}],"requestBody":{"required":true,"content":{"application/json":{"schema":{"$ref":"#/components/schemas/CreateSlot"}}}},"responses":{"201":{"description":"","content":{"application/json":{"schema":{"type":"object","properties":{"data":{"$ref":"#/components/schemas/SlotResource"}},"required":["data"]}}}},"404":{"$ref":"#/components/responses/ModelNotFoundException"},"401":{"$ref":"#/components/responses/AuthenticationException"},"422":{"$ref":"#/components/responses/ValidationException"},"403":{"$ref":"#/components/responses/AuthorizationException"},"429":{"description":"Rate limit exceeded. See Retry-After.","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"}}}},"409":{"description":"A business rule was violated: limits, or writing to a read-only slot type.","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"}}}}}}},"/profiles/{profileKey}/slots/{slot}":{"get":{"operationId":"getSlot","summary":"Get one slot.","tags":["Slots"],"parameters":[{"name":"profileKey","in":"path","required":true,"description":"Numeric profile id or login. A fully numeric segment is always treated as an id.","schema":{"type":"string"}},{"name":"slot","in":"path","required":true,"description":"The slot ID","schema":{"type":"integer"}}],"responses":{"200":{"description":"","content":{"application/json":{"schema":{"type":"object","properties":{"data":{"$ref":"#/components/schemas/SlotResource"}},"required":["data"]}}}},"404":{"$ref":"#/components/responses/ModelNotFoundException"},"401":{"$ref":"#/components/responses/AuthenticationException"},"422":{"$ref":"#/components/responses/ValidationException"},"403":{"$ref":"#/components/responses/AuthorizationException"},"429":{"description":"Rate limit exceeded. See Retry-After.","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"}}}}}},"patch":{"operationId":"updateSlot","summary":"Partially update slot data (top-level merge). The type cannot be changed.","tags":["Slots"],"parameters":[{"name":"profileKey","in":"path","required":true,"description":"Numeric profile id or login. A fully numeric segment is always treated as an id.","schema":{"type":"string"}},{"name":"slot","in":"path","required":true,"description":"The slot ID","schema":{"type":"integer"}},{"name":"Precognition","in":"header","description":"Set to \"true\" for a dry run: the request is validated and not executed (204 on success, 422 on failure).","schema":{"type":"string","enum":["true"]}}],"requestBody":{"required":true,"content":{"application/json":{"schema":{"$ref":"#/components/schemas/UpdateSlot"}}}},"responses":{"200":{"description":"","content":{"application/json":{"schema":{"type":"object","properties":{"data":{"$ref":"#/components/schemas/SlotResource"}},"required":["data"]}}}},"404":{"$ref":"#/components/responses/ModelNotFoundException"},"401":{"$ref":"#/components/responses/AuthenticationException"},"422":{"$ref":"#/components/responses/ValidationException"},"403":{"$ref":"#/components/responses/AuthorizationException"},"429":{"description":"Rate limit exceeded. See Retry-After.","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"}}}},"409":{"description":"A business rule was violated: limits, or writing to a read-only slot type.","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"}}}}}},"delete":{"operationId":"deleteSlot","summary":"Delete a slot. Permanent, there is no restore.","tags":["Slots"],"parameters":[{"name":"profileKey","in":"path","required":true,"description":"Numeric profile id or login. A fully numeric segment is always treated as an id.","schema":{"type":"string"}},{"name":"slot","in":"path","required":true,"description":"The slot ID","schema":{"type":"integer"}},{"name":"Precognition","in":"header","description":"Set to \"true\" for a dry run: the request is validated and not executed (204 on success, 422 on failure).","schema":{"type":"string","enum":["true"]}}],"responses":{"204":{"description":"Deleted","content":{"application/json":{"schema":{"type":"string"}}}},"404":{"$ref":"#/components/responses/ModelNotFoundException"},"401":{"$ref":"#/components/responses/AuthenticationException"},"422":{"$ref":"#/components/responses/ValidationException"},"403":{"$ref":"#/components/responses/AuthorizationException"},"429":{"description":"Rate limit exceeded. See Retry-After.","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"}}}},"409":{"description":"A business rule was violated: limits, or writing to a read-only slot type.","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"}}}}}}},"/profiles/{profileKey}/slots/order":{"put":{"operationId":"reorderSlots","summary":"Reorder slots. The list must contain every slot id of the profile exactly once.","tags":["Slots"],"parameters":[{"name":"profileKey","in":"path","required":true,"description":"Numeric profile id or login. A fully numeric segment is always treated as an id.","schema":{"type":"string"}},{"name":"Precognition","in":"header","description":"Set to \"true\" for a dry run: the request is validated and not executed (204 on success, 422 on failure).","schema":{"type":"string","enum":["true"]}}],"requestBody":{"required":true,"content":{"application/json":{"schema":{"$ref":"#/components/schemas/ReorderSlotsRequest"}}}},"responses":{"200":{"description":"","content":{"application/json":{"schema":{"type":"object","properties":{"data":{"type":"array","items":{"$ref":"#/components/schemas/SlotResource"}}},"required":["data"]}}}},"404":{"$ref":"#/components/responses/ModelNotFoundException"},"401":{"$ref":"#/components/responses/AuthenticationException"},"422":{"$ref":"#/components/responses/ValidationException"},"403":{"$ref":"#/components/responses/AuthorizationException"},"429":{"description":"Rate limit exceeded. See Retry-After.","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"}}}},"409":{"description":"A business rule was violated: limits, or writing to a read-only slot type.","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"}}}}}}},"/me":{"get":{"operationId":"getMe","description":"Call this first: it tells you whether the token can write, and how many links are still free, before you start creating anything.","summary":"Who this token belongs to, what it may do and how much of every limit is left.","tags":["Discovery"],"responses":{"200":{"description":"","content":{"application/json":{"schema":{"type":"object","properties":{"data":{"type":"object","properties":{"userId":{"type":"integer"},"name":{"type":"string"},"abilities":{"type":"array","items":{"type":"string"}},"canWrite":{"type":"boolean"},"limits":{"type":"object","properties":{"profiles":{"type":"object","properties":{"used":{"type":"integer"},"max":{"type":"integer"}},"required":["used","max"]},"slotsPerProfile":{"type":"integer"},"tokens":{"type":"object","properties":{"used":{"type":"integer"},"max":{"type":"integer"}},"required":["used","max"]},"rateLimit":{"type":"object","properties":{"requestsPerSecond":{"type":"integer"},"requestsPerMinute":{"type":"integer"},"musicLookupPerMinute":{"type":"integer"}},"required":["requestsPerSecond","requestsPerMinute","musicLookupPerMinute"]},"upload":{"type":"object","properties":{"maxMegabytes":{"type":"integer"},"maxMegabytesFromUrl":{"type":"integer"}},"required":["maxMegabytes","maxMegabytesFromUrl"]}},"required":["profiles","slotsPerProfile","tokens","rateLimit","upload"]}},"required":["userId","name","abilities","canWrite","limits"]}},"required":["data"]}}}},"401":{"$ref":"#/components/responses/AuthenticationException"},"429":{"description":"Rate limit exceeded. See Retry-After.","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"}}}}}}},"/slot-types":{"get":{"operationId":"listSlotTypes","summary":"JSON Schema, field descriptions and a working example for every slot type.","tags":["Discovery"],"responses":{"200":{"description":"","content":{"application/json":{"schema":{"type":"object","properties":{"data":{"type":"array","items":{"type":"object","properties":{"type":{"type":"string"},"title":{"type":"string"},"description":{"type":"string"},"writable":{"type":"boolean"},"schema":{"type":"object","additionalProperties":{}},"computed":{"type":"object","additionalProperties":{}},"enumLabels":{"type":"object","additionalProperties":{}},"example":{"type":"object","additionalProperties":{}}},"required":["type","title","description","writable","schema","computed","enumLabels","example"]}}},"required":["data"]}}}},"401":{"$ref":"#/components/responses/AuthenticationException"},"429":{"description":"Rate limit exceeded. See Retry-After.","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"}}}}}}},"/slot-types/{type}":{"get":{"operationId":"getSlotType","summary":"Description of one slot type.","tags":["Discovery"],"parameters":[{"name":"type","in":"path","required":true,"description":"Slot type.","schema":{"type":"string","enum":["button","picture","gallery","text","youtube","faq","divider","event","music"]}}],"responses":{"200":{"description":"","content":{"application/json":{"schema":{"type":"object","properties":{"data":{"type":"object","properties":{"type":{"type":"string"},"title":{"type":"string"},"description":{"type":"string"},"writable":{"type":"boolean"},"schema":{"type":"object","additionalProperties":{}},"computed":{"type":"object","additionalProperties":{}},"enumLabels":{"type":"object","additionalProperties":{}},"example":{"type":"object","additionalProperties":{}}},"required":["type","title","description","writable","schema","computed","enumLabels","example"]}},"required":["data"]}}}},"404":{"description":"","content":{"application/json":{"schema":{"type":"object","properties":{"message":{"type":"string"},"code":{"type":"string","const":"not_found"},"errors":{"type":"null"},"documentation":{"type":"string"}},"required":["message","code","errors","documentation"]}}}},"401":{"$ref":"#/components/responses/AuthenticationException"},"429":{"description":"Rate limit exceeded. See Retry-After.","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"}}}}}}},"/":{"get":{"operationId":"getApiIndex","description":"Public: no token required. Start here when you do not know the API yet.","summary":"Entry point: where the docs, the discovery endpoints and the token settings are.","tags":["Discovery"],"responses":{"200":{"description":"","content":{"application/json":{"schema":{"type":"object","properties":{"data":{"type":"object","properties":{"name":{"type":"string","const":"Tapter Profile API"},"version":{"type":"string","const":"v1"},"baseUrl":{"type":"string"},"authentication":{"type":"object","properties":{"scheme":{"type":"string","const":"Bearer"},"header":{"type":"string","const":"Authorization: Bearer <token>"},"tokenPrefix":{"type":"string"},"issuedAt":{"type":"string"},"abilities":{"type":"object","properties":{"profile:read":{"type":"string","const":"Read profiles, slots and discovery."},"profile:write":{"type":"string","const":"Create, change and delete slots, update profiles, upload files."}},"required":["profile:read","profile:write"]}},"required":["scheme","header","tokenPrefix","issuedAt","abilities"]},"discovery":{"type":"object","properties":{"me":{"type":"string","description":"Call this first once you have a token: it answers \"what may I do and how much is left\"."},"slotTypes":{"type":"string"},"profileSettings":{"type":"string","description":"Login and page language, together with the list of active languages."},"openapi":{"type":"string"},"profileMarkdown":{"type":"string","description":"The finished page as a language model sees it — how you check your own work."},"musicLookup":{"type":"string","description":"Required before creating a music slot: the platform links cannot be guessed."}},"required":["me","slotTypes","profileSettings","openapi","profileMarkdown","musicLookup"]},"conventions":{"type":"object","properties":{"envelope":{"type":"string","const":"Success: {data, meta?}. Error: {message, code, errors?, documentation?}."},"keys":{"type":"string","const":"camelCase"},"dates":{"type":"string","const":"ISO 8601"},"strictWrite":{"type":"string","const":"Unknown or server-computed fields in data return 422 listing every offending key."},"tolerantRead":{"type":"string","const":"Legacy slots are returned with \"supported\": false and can be deleted or reordered, but not edited."},"dryRun":{"type":"string","const":"Send \"Precognition: true\" on any write request to validate without executing."},"login":{"type":"string","description":"Changing the login changes the public address, and the old one is not redirected."},"idempotency":{"type":"string","const":"Send \"Idempotency-Key: <string>\" on POST. Repeating it within 24h returns the stored response."},"images":{"type":"string","description":"Proportions are not cosmetic: the avatar is cropped to a circle, the background is not cropped at all."},"preview":{"type":"string"}},"required":["envelope","keys","dates","strictWrite","tolerantRead","dryRun","login","idempotency","images","preview"]},"limits":{"type":"object","properties":{"requestsPerSecond":{"type":"integer","const":5},"requestsPerMinute":{"type":"integer","const":60},"slotsPerProfile":{"type":"integer","const":100},"profilesPerUser":{"type":"integer","const":100},"tokensPerUser":{"type":"integer","const":5}},"required":["requestsPerSecond","requestsPerMinute","slotsPerProfile","profilesPerUser","tokensPerUser"]},"examples":{"type":"object","properties":{"readProfile":{"type":"string"},"addButton":{"type":"string"},"reorderSlots":{"type":"string"},"addMusic":{"type":"string","description":"Music blocks are two steps: look the release up first, then POST the returned data.slot."},"whoAmI":{"type":"string"},"previewPage":{"type":"string"},"uploadByUrl":{"type":"string"},"changeLanguage":{"type":"string"}},"required":["readProfile","addButton","reorderSlots","addMusic","whoAmI","previewPage","uploadByUrl","changeLanguage"]},"notAvailable":{"type":"array","prefixItems":[{"type":"string","const":"Deleting a profile (link) — dashboard only, it is irreversible."},{"type":"string","const":"Profile themes — not open in the dashboard yet, so the API neither returns nor accepts them."},{"type":"string","const":"Events and storefront catalogs — event slots are read-only."},{"type":"string","const":"Batch replace of all slots — use POST/PATCH/DELETE per slot."}],"minItems":4,"maxItems":4,"additionalItems":false}},"required":["name","version","baseUrl","authentication","discovery","conventions","limits","examples","notAvailable"]}},"required":["data"]}}}},"429":{"description":"Rate limit exceeded. See Retry-After.","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"}}}}},"security":[{}]}},"/music/lookup":{"get":{"operationId":"lookupMusic","description":"Required step before creating a music slot: the platform links cannot be guessed. The response contains data.slot — the body of POST /profiles/{profile}/slots, ready to be forwarded as-is. Rate limited to 15 requests per minute on top of the global limit: every call hits the platform APIs.","summary":"Resolve a release by UPC, ISRC, a link to any music platform, or a title, and get the links for every supported platform.","tags":["Music"],"parameters":[{"name":"query","in":"query","required":true,"description":"UPC (12–13 digits), ISRC (12 characters), a release URL on any music platform,\nor a release title.","schema":{"type":"string","minLength":1,"maxLength":500}},{"name":"artist","in":"query","description":"Narrows down a title search. Ignored when query is a UPC, an ISRC or a URL.","schema":{"type":["string","null"],"maxLength":200}},{"name":"type","in":"query","description":"What a title + artist search looks for. Ignored otherwise.","schema":{"type":["string","null"],"enum":["album","track",null]}}],"responses":{"200":{"description":"","content":{"application/json":{"schema":{"type":"object","properties":{"data":{"type":"object","properties":{"query":{"type":"string"},"matchedBy":{"type":"string"},"album":{"type":["string","null"]},"artist":{"type":["string","null"]},"thumbnail":{"type":["string","null"]},"links":{"type":"array","items":{"type":"object","properties":{"platform":{"type":"string"},"label":{"type":"string"},"icon":{"type":"string"},"url":{"type":"string"}},"required":["platform","label","icon","url"]}},"slot":{"type":"object","properties":{"type":{"type":"string"},"data":{"type":"object","additionalProperties":{}}},"required":["type","data"]}},"required":["query","matchedBy","album","artist","thumbnail","links","slot"]}},"required":["data"]}}}},"404":{"description":"","content":{"application/json":{"schema":{"type":"object","properties":{"message":{"type":"string","const":"По этому запросу релиз не найден. Пришлите UPC, ISRC, ссылку на релиз на любой музыкальной площадке или название релиза (можно вместе с параметром artist)."},"code":{"type":"string","const":"not_found"},"errors":{"type":"null"},"documentation":{"type":"string"}},"required":["message","code","errors","documentation"]}}}},"401":{"$ref":"#/components/responses/AuthenticationException"},"422":{"$ref":"#/components/responses/ValidationException"},"403":{"$ref":"#/components/responses/AuthorizationException"},"429":{"description":"Rate limit exceeded. See Retry-After.","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"}}}}}}},"/files":{"post":{"operationId":"uploadFile","description":"Two ways to send the image. Multipart form-data with a \"file\" part, up to 50 MB. Or JSON {\"url\": \"https://…\"} and the server downloads it: http/https only, up to 10 MB, 10 s timeout, and the bytes have to be a real jpeg, png, webp or gif. Send one of the two, not both.","summary":"Upload an image and get the path to use in slots and profile fields.","tags":["Files"],"parameters":[{"name":"Idempotency-Key","in":"header","description":"Repeating the same key within 24 hours returns the stored response and creates nothing. A different body with the same key returns 422.","schema":{"type":"string","maxLength":128}},{"name":"Precognition","in":"header","description":"Set to \"true\" for a dry run: the request is validated and not executed (204 on success, 422 on failure).","schema":{"type":"string","enum":["true"]}}],"requestBody":{"content":{"multipart/form-data":{"schema":{"$ref":"#/components/schemas/StoreFileRequest"}},"application/json":{"schema":{"type":"object","required":["url"],"properties":{"url":{"type":"string","format":"uri","maxLength":2048,"description":"Direct link to the image. The server downloads it: http/https only, default ports only, up to 10 MB, 10 s timeout, and the bytes have to decode as image/jpeg, image/png, image/webp, image/gif. Links resolving to a private network address are refused."}},"additionalProperties":false}}}},"responses":{"201":{"description":"","content":{"application/json":{"schema":{"type":"object","properties":{"data":{"type":"object","properties":{"path":{"type":"string"},"src":{"type":"string"},"width":{"type":["integer","null"]},"height":{"type":["integer","null"]}},"required":["path","src","width","height"]}},"required":["data"]}}}},"422":{"$ref":"#/components/responses/ValidationException"},"401":{"$ref":"#/components/responses/AuthenticationException"},"403":{"$ref":"#/components/responses/AuthorizationException"},"429":{"description":"Rate limit exceeded. See Retry-After.","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"}}}},"409":{"description":"A business rule was violated: limits, or writing to a read-only slot type.","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"}}}}}}}},"components":{"securitySchemes":{"bearerAuth":{"type":"http","description":"Sanctum personal access token, prefix tptr_.","scheme":"bearer"}},"schemas":{"CreateSlot":{"oneOf":[{"type":"object","required":["type"],"properties":{"type":{"type":"string","const":"button"},"data":{"$ref":"#/components/schemas/SlotData_Button"},"order":{"type":"integer","minimum":0}}},{"type":"object","required":["type"],"properties":{"type":{"type":"string","const":"picture"},"data":{"$ref":"#/components/schemas/SlotData_Picture"},"order":{"type":"integer","minimum":0}}},{"type":"object","required":["type"],"properties":{"type":{"type":"string","const":"gallery"},"data":{"$ref":"#/components/schemas/SlotData_Gallery"},"order":{"type":"integer","minimum":0}}},{"type":"object","required":["type"],"properties":{"type":{"type":"string","const":"text"},"data":{"$ref":"#/components/schemas/SlotData_Text"},"order":{"type":"integer","minimum":0}}},{"type":"object","required":["type"],"properties":{"type":{"type":"string","const":"youtube"},"data":{"$ref":"#/components/schemas/SlotData_Youtube"},"order":{"type":"integer","minimum":0}}},{"type":"object","required":["type"],"properties":{"type":{"type":"string","const":"faq"},"data":{"$ref":"#/components/schemas/SlotData_Faq"},"order":{"type":"integer","minimum":0}}},{"type":"object","required":["type"],"properties":{"type":{"type":"string","const":"divider"},"data":{"$ref":"#/components/schemas/SlotData_Divider"},"order":{"type":"integer","minimum":0}}},{"type":"object","required":["type"],"properties":{"type":{"type":"string","const":"music"},"data":{"$ref":"#/components/schemas/SlotData_Music"},"order":{"type":"integer","minimum":0}}}],"discriminator":{"propertyName":"type"},"title":"CreateSlot"},"Error":{"type":"object","required":["message","code"],"properties":{"message":{"type":"string"},"code":{"type":"string"},"errors":{"type":"object","additionalProperties":{"type":"array","items":{"type":"string"}}},"documentation":{"type":"string"}},"title":"Error"},"ProfileResource":{"type":"object","properties":{"id":{"type":"integer"},"login":{"type":"string"},"title":{"type":["string","null"]},"description":{"type":["string","null"]},"avatar":{"type":["string","null"],"description":"File path, not a URL. See computed.avatarUrl. Rendered as a circle, so a square image\nis expected."},"bgImage":{"type":["string","null"]},"socials":{"type":"array","description":"Icons are derived by the server and reported in computed.socials, not here: sending\nan icon back in a PATCH returns 422.","items":{"type":"object","properties":{"link":{"type":"string"},"order":{"type":"integer"}},"required":["link","order"]}},"themeId":{"type":"integer"},"languageId":{"type":["integer","null"]},"computed":{"type":"object","properties":{"url":{"type":"string"},"avatarUrl":{"type":["string","null"]},"bgImageUrl":{"type":["string","null"]},"socials":{"type":"array","items":{"type":"object","properties":{"icon":{"type":"string"}},"required":["icon"]}},"isMain":{"type":"boolean"},"status":{"type":"boolean"}},"required":["url","avatarUrl","bgImageUrl","socials","isMain","status"]},"slots":{"type":"array","items":{"$ref":"#/components/schemas/SlotResource"}}},"required":["id","login","title","description","avatar","bgImage","socials","themeId","languageId","computed"],"title":"ProfileResource"},"ProfileSettingsResource":{"type":"object","properties":{"profileId":{"type":"integer"},"login":{"type":"string"},"languageId":{"type":["integer","null"]},"languageCode":{"type":["string","null"]},"computed":{"type":"object","properties":{"url":{"type":"string"},"effectiveLanguageCode":{"type":"string","description":"The language the page actually renders in. With languageId null it is the\nlanguage of the owner."}},"required":["url","effectiveLanguageCode"]},"options":{"type":"object","properties":{"languages":{"type":"array","items":{"type":"object","properties":{"id":{"type":"integer"},"code":{"type":"string"},"name":{"type":"string"}},"required":["id","code","name"]}},"login":{"type":"object","properties":{"minLength":{"type":"integer","const":4},"maxLength":{"type":"integer","const":60},"pattern":{"type":"string","const":"^[a-zA-Z0-9._]+$"},"reserved":{"type":"array","prefixItems":[{"type":"string","const":"adm"},{"type":"string","const":"agreements"},{"type":"string","const":"api"},{"type":"string","const":"auth"},{"type":"string","const":"email"},{"type":"string","const":"event"},{"type":"string","const":"events"},{"type":"string","const":"filament"},{"type":"string","const":"forgot-password"},{"type":"string","const":"google"},{"type":"string","const":"login"},{"type":"string","const":"logout"},{"type":"string","const":"my-links"},{"type":"string","const":"my-orders"},{"type":"string","const":"order"},{"type":"string","const":"profile"},{"type":"string","const":"qr"},{"type":"string","const":"register"},{"type":"string","const":"reset-password"},{"type":"string","const":"rsvp"},{"type":"string","const":"sanctum"},{"type":"string","const":"settings"},{"type":"string","const":"shop"},{"type":"string","const":"sitemap"},{"type":"string","const":"terms"},{"type":"string","const":"validate"},{"type":"string","const":"verify-email"},{"type":"string","const":"webhook"}],"minItems":28,"maxItems":28,"additionalItems":false}},"required":["minLength","maxLength","pattern","reserved"]}},"required":["languages","login"]}},"required":["profileId","login","languageId","languageCode","computed","options"],"title":"ProfileSettingsResource"},"ReorderSlotsRequest":{"type":"object","properties":{"order":{"type":"array","description":"Must contain every slot id of the profile exactly once, in the new order.","items":{"type":"integer","description":"Slot id."},"minItems":1,"uniqueItems":true}},"required":["order"],"title":"ReorderSlotsRequest"},"SlotData_Button":{"$schema":"https://json-schema.org/draft/2020-12/schema","type":"object","required":["link"],"additionalProperties":false,"properties":{"title":{"type":"string","maxLength":120,"default":"","description":"Text on the button. Required unless subtitle is set."},"subtitle":{"type":"string","maxLength":120,"default":"","description":"Small caption under the button text."},"link":{"type":"string","maxLength":2048,"description":"Destination. A missing scheme is completed to https://. Allowed schemes: http, https, tel, mailto, whatsapp."},"style":{"type":"string","enum":["primary","bright","light","classic-dark","dull-gray","very-red","honey-yellow","money-green","grass-green","social-blue","outdated-coral","seems-lilac","pinkish","custom"],"default":"primary","description":"One of the preset styles, or \"custom\" to set your own colors in the customize object."},"customize":{"type":["object","null"],"default":null,"description":"Custom colors. Required when style is \"custom\", ignored (stored as null) otherwise.","additionalProperties":false,"properties":{"backgroundColor":{"type":"string","maxLength":32,"default":"#424248"},"textColor":{"type":"string","maxLength":32,"default":"#FFFFFF"},"subtitleColor":{"type":"string","maxLength":32,"default":"#E0E7FF"},"borderRadius":{"type":"integer","minimum":0,"maximum":50,"default":12}}}},"title":"SlotData_Button"},"SlotData_Divider":{"$schema":"https://json-schema.org/draft/2020-12/schema","type":"object","required":[],"additionalProperties":false,"properties":[],"title":"SlotData_Divider"},"SlotData_Event":{"$schema":"https://json-schema.org/draft/2020-12/schema","type":"object","required":["eventId"],"additionalProperties":false,"properties":{"eventId":{"type":"integer","description":"Id of the linked event."}},"title":"SlotData_Event"},"SlotData_Faq":{"$schema":"https://json-schema.org/draft/2020-12/schema","type":"object","required":["items"],"additionalProperties":false,"properties":{"title":{"type":"string","maxLength":200,"default":"","description":"Heading shown above the list."},"faqStyle":{"type":"string","enum":["border-faq","plain-faq"],"default":"border-faq","description":"Visual style of the rows."},"items":{"type":"array","minItems":1,"maxItems":50,"description":"List rows, 1 to 50.","items":{"type":"object","required":["title","text"],"additionalProperties":false,"properties":{"id":{"type":"string","maxLength":64,"description":"Row id. Generated by the server when omitted; the dashboard addresses rows by it."},"title":{"type":"string","maxLength":300,"description":"Question. Plain text, must not be empty."},"text":{"type":"string","maxLength":5000,"description":"Answer. Plain text, must not be empty."}}}}},"title":"SlotData_Faq"},"SlotData_Gallery":{"$schema":"https://json-schema.org/draft/2020-12/schema","type":"object","required":["items"],"additionalProperties":false,"properties":{"title":{"type":"string","maxLength":120,"default":"","description":"Heading shown above the gallery."},"items":{"type":"array","minItems":1,"maxItems":30,"description":"Gallery images, 1 to 30.","items":{"type":"object","required":["path"],"additionalProperties":false,"properties":{"path":{"type":"string","maxLength":1024,"description":"File path from POST /files. Must belong to the user and be unique within the slot."},"text":{"type":"string","maxLength":1000,"default":"","description":"Caption under the image and inside the lightbox. Plain text."},"link":{"type":"string","maxLength":2048,"default":"","description":"Optional destination for this image."}}}}},"title":"SlotData_Gallery"},"SlotData_Music":{"$schema":"https://json-schema.org/draft/2020-12/schema","type":"object","required":["links"],"additionalProperties":false,"properties":{"mode":{"type":"string","enum":["auto","manual"],"default":"manual","description":"Which section the dashboard dialog opens on. Does not affect rendering; keep \"manual\" for slots created through the API."},"style":{"type":"string","enum":["tapter","bubble","light","gradient","brutal","cyber","minimal"],"default":"tapter","description":"Visual style of the card."},"album":{"type":"string","maxLength":200,"default":"","description":"Release title."},"artist":{"type":"string","maxLength":200,"default":"","description":"Artist name."},"thumbnail":{"type":"string","maxLength":2048,"default":"","description":"Cover image URL (http/https)."},"showCover":{"type":"boolean","default":true,"description":"Whether to show the cover image."},"links":{"type":"array","minItems":1,"maxItems":15,"description":"Platform links, 1 to 15.","items":{"type":"object","required":["link"],"additionalProperties":false,"properties":{"link":{"type":"string","maxLength":2048,"description":"Link to the release on a platform. The icon is detected from the host."},"order":{"type":"integer","minimum":0,"description":"Position of the link. Defaults to its index in the array."}}}}},"title":"SlotData_Music"},"SlotData_Picture":{"$schema":"https://json-schema.org/draft/2020-12/schema","type":"object","required":["path"],"additionalProperties":false,"properties":{"path":{"type":"string","maxLength":1024,"description":"File path returned by POST /files. Not a URL. Must belong to the authenticated user."},"link":{"type":"string","maxLength":2048,"default":"","description":"Optional destination opened when the image is tapped."},"size":{"type":"string","enum":["large","medium","small"],"default":"large","description":"Rendered size of the image."}},"title":"SlotData_Picture"},"SlotData_Text":{"$schema":"https://json-schema.org/draft/2020-12/schema","type":"object","required":[],"additionalProperties":false,"properties":{"title":{"type":"string","maxLength":200,"default":"","description":"Heading. Required unless description is set."},"description":{"type":"string","maxLength":20000,"default":"","description":"Body text. Limited HTML is kept (p, ul, ol, li, strong, em, s, u, span, br); everything else is stripped on save."},"textStyle":{"type":"string","enum":["border","plain"],"default":"border","description":"Visual style: \"border\" renders a card, \"plain\" renders plain text."},"link":{"type":"string","maxLength":2048,"default":"","description":"Optional destination for the whole block."}},"title":"SlotData_Text"},"SlotData_Youtube":{"$schema":"https://json-schema.org/draft/2020-12/schema","type":"object","required":["link"],"additionalProperties":false,"properties":{"title":{"type":"string","maxLength":200,"default":"","description":"Heading shown above the video."},"description":{"type":"string","maxLength":2000,"default":"","description":"Plain text below the video. HTML is stripped."},"link":{"type":"string","maxLength":2048,"description":"Any YouTube URL. The video id is extracted server-side."}},"title":"SlotData_Youtube"},"SlotResource":{"type":"object","properties":{"id":{"type":"integer"},"type":{"type":"string","description":"Slot type. Determines the shape of data; see GET /slot-types."},"order":{"type":"integer"},"supported":{"type":"string","description":"false — legacy slot type: it can be deleted or reordered, but not edited."},"data":{"type":"string","description":"Body of the slot, in the shape of the schema of its type."},"computed":{"type":"string","description":"Server-derived, read-only. Sending any of these fields in data returns 422."},"message":{"type":"string","const":"Этот тип слота не поддерживается API. Его можно удалить или переставить, но не редактировать."},"createdAt":{"type":["string","null"]},"updatedAt":{"type":["string","null"]}},"required":["id","type","order","supported","data","computed","createdAt","updatedAt"],"title":"SlotResource"},"StoreFileRequest":{"type":"object","properties":{"file":{"type":"string","format":"binary","contentMediaType":"application/octet-stream","description":"Два взаимоисключающих способа: multipart-файл или ссылка. Ссылка — для тул-раннеров,\nкоторые multipart не умеют собирать; лимиты у неё свои и строже (см. RemoteImageFetcher)."},"url":{"type":"string","format":"uri","description":"jpg, jpeg, png, webp or gif, up to 50 MB when sent as a multipart file. Everything except\ngif is also downscaled to 800px width, so anything above ~1600px wide brings nothing.\nProportions that fit where the image is going: profile avatar — square 1:1 (rendered as a\ncircle), profile bgImage — 16:9 landscape (rendered full width, uncropped), picture and\ngallery slots — any.","maxLength":2048}},"title":"StoreFileRequest"},"StoreProfileRequest":{"type":"object","properties":{"login":{"type":"string","description":"The public address of the page, tapter.one/<login>.","pattern":"^[a-zA-Z0-9._]+$","minLength":4,"maxLength":60}},"required":["login"],"title":"StoreProfileRequest"},"UpdateProfileRequest":{"type":"object","description":"Fields of a profile that can be changed through the API. Omitted fields are left untouched.","properties":{"title":{"type":["string","null"],"description":"Shown under the avatar, above the content blocks.","maxLength":200},"description":{"type":["string","null"],"description":"Plain text; rendered as nl2br(strip_tags(...)).","maxLength":5000},"avatar":{"type":["string","null"],"description":"File path from POST /files. Rendered as a circle: use a square (1:1) image, 800x800 or\nlarger. A non-square image gets cropped to its center, so anything off-center is lost.","maxLength":1024},"bgImage":{"type":["string","null"],"description":"File path from POST /files. Optional, and a page without it is a normal page, not an\nunfinished one: do not add or generate a background unless the user asked for one — ask\nfirst. Rendered full width above the avatar, at its own aspect ratio and without cropping:\nuse a 16:9 landscape image, 1600x900 or larger. A square or portrait image is not cropped —\nit pushes the title, the description and the whole content of the page below the fold.\nA busy background also competes with the content blocks below it, so keep it calm.","maxLength":1024},"socials":{"type":["array","null"],"description":"Social links rendered as a row of icons; the icon is derived from the host by the server.","items":{"type":"object","properties":{"link":{"type":"string","maxLength":2048},"order":{"type":["integer","null"],"minimum":0}},"required":["link"]},"maxItems":20}},"title":"UpdateProfileRequest"},"UpdateSettingsRequest":{"type":"object","description":"Settings of a profile: the public login and the language of the page.\nOmitted fields are left untouched.","properties":{"login":{"type":"string","description":"The public URL of the page. Changing it takes effect immediately and the old address is\nnot redirected, so existing links, QR codes and NFC tags break. Change it only when the\nuser asked to, and check availability with GET /profiles/check-login?login=<login>&profile=<this profile>\nfirst. The current login of this very profile is accepted and changes nothing.","pattern":"^[a-zA-Z0-9._]+$","minLength":4,"maxLength":60},"languageId":{"type":["integer","null"],"description":"Id of an active language, see options.languages. null resets the page to the language of\nthe owner. Mutually exclusive with languageCode."},"languageCode":{"type":["string","null"],"description":"The same thing by code, e.g. \"ru\", \"kk\", \"en\". Mutually exclusive with languageId.","maxLength":10}},"title":"UpdateSettingsRequest"},"UpdateSlot":{"type":"object","properties":{"data":{"type":"object","description":"Subset of the slot data schema; merged over the current values at the top level."},"order":{"type":"integer","minimum":0}},"title":"UpdateSlot"}},"responses":{"AuthenticationException":{"description":"Unauthenticated","content":{"application/json":{"schema":{"type":"object","properties":{"message":{"type":"string","description":"Error overview."}},"required":["message"]}}}},"ValidationException":{"description":"Validation error","content":{"application/json":{"schema":{"type":"object","properties":{"message":{"type":"string","description":"Errors overview."},"errors":{"type":"object","description":"A detailed description of each field that failed validation.","additionalProperties":{"type":"array","items":{"type":"string"}}}},"required":["message","errors"]}}}},"AuthorizationException":{"description":"Authorization error","content":{"application/json":{"schema":{"type":"object","properties":{"message":{"type":"string","description":"Error overview."}},"required":["message"]}}}},"ModelNotFoundException":{"description":"Not found","content":{"application/json":{"schema":{"type":"object","properties":{"message":{"type":"string","description":"Error overview."}},"required":["message"]}}}}}}}