{"openapi":"3.1.0","info":{"title":"Clavian API","version":"1.0.0","description":"The internal contract the consoles, apps, kiosk and station use. Generated from the Zod schemas in @clavian/contracts; never edited by hand. See spec/08-api-design.md."},"servers":[{"url":"https://api.clavian.io","description":"Production"}],"tags":[{"name":"meta","description":"Health, metadata and the contract smoke endpoint."}],"components":{"schemas":{"Money":{"type":"object","properties":{"amount":{"type":"integer","minimum":-10000000000000,"maximum":10000000000000,"description":"An integer number of the currency's minor units.","example":15000},"currency":{"$ref":"#/components/schemas/CurrencyCode"}},"required":["amount","currency"],"description":"An amount in a currency's minor unit. { amount: 15000, currency: 'INR' } is ₹150.00."},"CurrencyCode":{"type":"string","pattern":"^[A-Z]{3}$","description":"ISO 4217 alphabetic currency code.","example":"INR"},"NonNegativeMoney":{"type":"object","properties":{"amount":{"type":"integer","minimum":0,"maximum":10000000000000},"currency":{"$ref":"#/components/schemas/CurrencyCode"}},"required":["amount","currency"],"description":"Money that may be zero but never negative."},"PositiveMoney":{"type":"object","properties":{"amount":{"type":"integer","minimum":1,"maximum":10000000000000},"currency":{"$ref":"#/components/schemas/CurrencyCode"}},"required":["amount","currency"],"description":"Money strictly greater than zero."},"Page":{"type":"object","properties":{"cursor":{"type":["string","null"],"minLength":1},"hasMore":{"type":"boolean"},"count":{"type":"integer","minimum":0,"maximum":100}},"required":["cursor","hasMore","count"],"description":"Cursor pagination state for a collection response."},"Problem":{"type":"object","properties":{"type":{"type":"string","format":"uri"},"title":{"type":"string","minLength":1},"status":{"type":"integer","minimum":400,"maximum":599},"detail":{"type":"string"},"code":{"type":"string","pattern":"^[a-z][a-z0-9]*(?:_[a-z0-9]+)*$"},"requestId":{"$ref":"#/components/schemas/RequestId"},"errors":{"type":"array","items":{"$ref":"#/components/schemas/ProblemFieldError"}},"instance":{"type":"string"}},"required":["type","title","status","code","requestId"],"description":"An RFC 9457 problem document. Every error response in the API has this shape."},"RequestId":{"type":"string","pattern":"^req_[0-7][0123456789abcdefghjkmnpqrstvwxyz]{25}$","description":"A TypeID: \"req_\" followed by the base32 form of a UUIDv7.","example":"req_01k5znw4hmfay8vvr14d2pf2db"},"ProblemFieldError":{"type":"object","properties":{"field":{"type":"string","minLength":1},"code":{"type":"string","minLength":1},"message":{"type":"string","minLength":1}},"required":["field","code","message"],"description":"A field-level validation problem."},"AuditStamp":{"type":"object","properties":{"by":{"$ref":"#/components/schemas/ActorRef"},"at":{"$ref":"#/components/schemas/IsoDateTime"}},"required":["by","at"],"description":"Who changed something and when."},"ActorRef":{"type":["string","null"],"pattern":"^[a-z][a-z_]*_[0-7][0123456789abcdefghjkmnpqrstvwxyz]{25}$","description":"The TypeID of whoever acted, or null when the platform did."},"IsoDateTime":{"type":"string","format":"date-time","description":"RFC 3339 timestamp in UTC.","example":"2026-09-20T12:00:00.120Z"},"Address":{"type":"object","properties":{"line1":{"type":"string","minLength":1,"maxLength":120},"line2":{"type":"string","maxLength":120},"landmark":{"type":"string","maxLength":120},"locality":{"type":"string","minLength":1,"maxLength":80},"region":{"type":"string","minLength":1,"maxLength":80},"postalCode":{"type":"string","minLength":3,"maxLength":16},"country":{"$ref":"#/components/schemas/CountryCode"}},"required":["line1","locality","region","postalCode","country"],"description":"A postal address."},"CountryCode":{"type":"string","pattern":"^[A-Z]{2}$","description":"ISO 3166-1 alpha-2 country code.","example":"IN"},"Coordinate":{"type":"object","properties":{"lat":{"type":"number","minimum":-90,"maximum":90},"lng":{"type":"number","minimum":-180,"maximum":180}},"required":["lat","lng"],"description":"A WGS 84 latitude and longitude in degrees."},"PhoneNumber":{"type":"string","pattern":"^\\+[1-9]\\d{1,14}$","description":"A phone number in E.164 form: \"+\" then the country calling code then the number.","example":"+919876543210"},"TimeWindow":{"type":"object","properties":{"days":{"type":"array","items":{"$ref":"#/components/schemas/TimeWindowDay"},"minItems":1,"maxItems":8},"start":{"$ref":"#/components/schemas/LocalTime"},"end":{"$ref":"#/components/schemas/LocalTime"}},"required":["days","start","end"],"description":"A recurring time of day in the venue's timezone. `end` earlier than `start` means the window crosses midnight."},"TimeWindowDay":{"type":"string","enum":["mon","tue","wed","thu","fri","sat","sun","holiday"],"description":"A weekday, or the venue's holiday calendar."},"LocalTime":{"type":"string","pattern":"^(?:[01]\\d|2[0-3]):[0-5]\\d$","description":"A 24-hour time of day in the venue's timezone.","example":"18:30"},"Scope":{"type":"object","properties":{"organizationId":{"$ref":"#/components/schemas/OrganizationId"},"venueId":{"$ref":"#/components/schemas/VenueId"}},"required":["organizationId"],"description":"The organisation, and optionally the venue, a request applies to."},"OrganizationId":{"type":"string","pattern":"^org_[0-7][0123456789abcdefghjkmnpqrstvwxyz]{25}$","description":"A TypeID: \"org_\" followed by the base32 form of a UUIDv7.","example":"org_01k5znw4hmfay8vvr14d2pf2db"},"VenueId":{"type":"string","pattern":"^ven_[0-7][0123456789abcdefghjkmnpqrstvwxyz]{25}$","description":"A TypeID: \"ven_\" followed by the base32 form of a UUIDv7.","example":"ven_01k5znw4hmfay8vvr14d2pf2db"},"VenuePhoto":{"type":"object","properties":{"fileId":{"$ref":"#/components/schemas/FileId"},"url":{"type":"string","format":"uri"},"variants":{"type":"array","items":{"$ref":"#/components/schemas/ImageVariant"}}},"required":["fileId","url","variants"]},"FileId":{"type":"string","pattern":"^fil_[0-7][0123456789abcdefghjkmnpqrstvwxyz]{25}$","description":"A TypeID: \"fil_\" followed by the base32 form of a UUIDv7.","example":"fil_01k5znw4hmfay8vvr14d2pf2db"},"ImageVariant":{"type":"object","properties":{"width":{"type":"integer","exclusiveMinimum":0},"url":{"type":"string","format":"uri"}},"required":["width","url"]},"IdCheckDocument":{"type":"object","properties":{"type":{"$ref":"#/components/schemas/IdDocumentType"},"last4":{"type":"string"}},"required":["type","last4"],"description":"The document a staff member looked at."},"IdDocumentType":{"type":"string","enum":["aadhaar","pan","passport","driving_licence","voter_id","other"]},"IdCheckVerifier":{"type":"object","properties":{"name":{"type":["string","null"]}},"required":["name"],"description":"Who checked the document."},"IdCheckPhoto":{"type":"object","properties":{"contentType":{"$ref":"#/components/schemas/ImageContentType"},"deletedAt":{"allOf":[{"$ref":"#/components/schemas/IsoDateTime"},{"type":["string","null"]}]}},"required":["contentType","deletedAt"],"description":"The photo taken with a check."},"ImageContentType":{"type":"string","enum":["image/jpeg","image/png","image/webp"]},"SharedDeviceHolder":{"type":"object","properties":{"memberId":{"$ref":"#/components/schemas/MemberId"},"name":{"type":["string","null"]},"role":{"$ref":"#/components/schemas/RoleName"}},"required":["memberId","name","role"],"description":"Who holds the PIN session on a counter device."},"MemberId":{"type":"string","pattern":"^mem_[0-7][0123456789abcdefghjkmnpqrstvwxyz]{25}$","description":"A TypeID: \"mem_\" followed by the base32 form of a UUIDv7.","example":"mem_01k5znw4hmfay8vvr14d2pf2db"},"RoleName":{"type":"string","pattern":"^[a-z][a-z0-9_]{0,63}$","description":"`owner`, `manager`, `cashier`, … or a custom role of the organisation."},"HelloResponse":{"type":"object","properties":{"data":{"type":"array","items":{"$ref":"#/components/schemas/Greeting"}},"page":{"$ref":"#/components/schemas/Page"}},"required":["data","page"]},"Greeting":{"type":"object","properties":{"id":{"$ref":"#/components/schemas/MessageId"},"greeting":{"type":"string","minLength":1,"maxLength":120},"at":{"$ref":"#/components/schemas/IsoDateTime"},"sampleAmount":{"$ref":"#/components/schemas/Money"}},"required":["id","greeting","at","sampleAmount"],"description":"One greeting from the contract smoke endpoint."},"MessageId":{"type":"string","pattern":"^msg_[0-7][0123456789abcdefghjkmnpqrstvwxyz]{25}$","description":"A TypeID: \"msg_\" followed by the base32 form of a UUIDv7.","example":"msg_01k5znw4hmfay8vvr14d2pf2db"},"ScopeProbeList":{"type":"object","properties":{"data":{"type":"array","items":{"$ref":"#/components/schemas/ScopeProbe"}},"page":{"$ref":"#/components/schemas/Page"}},"required":["data","page"]},"ScopeProbe":{"type":"object","properties":{"id":{"$ref":"#/components/schemas/ScopeProbeId"},"organizationId":{"type":"string","description":"The organisation the row belongs to."},"venueId":{"allOf":[{"$ref":"#/components/schemas/VenueId"},{"type":["string","null"]}]},"label":{"type":"string","minLength":1,"maxLength":120},"createdAt":{"$ref":"#/components/schemas/IsoDateTime"}},"required":["id","organizationId","venueId","label","createdAt"],"description":"One row of the organisation-scoped smoke resource."},"ScopeProbeId":{"type":"string","pattern":"^prb_[0-7][0123456789abcdefghjkmnpqrstvwxyz]{25}$","description":"A TypeID: \"prb_\" followed by the base32 form of a UUIDv7.","example":"prb_01k5znw4hmfay8vvr14d2pf2db"},"CreateScopeProbe":{"type":"object","properties":{"label":{"type":"string","minLength":1,"maxLength":120}},"required":["label"]},"AuditEntryList":{"type":"object","properties":{"data":{"type":"array","items":{"$ref":"#/components/schemas/AuditEntry"}},"page":{"$ref":"#/components/schemas/Page"}},"required":["data","page"]},"AuditEntry":{"type":"object","properties":{"id":{"$ref":"#/components/schemas/AuditEntryId"},"occurredAt":{"$ref":"#/components/schemas/IsoDateTime"},"recordedAt":{"$ref":"#/components/schemas/IsoDateTime"},"actor":{"$ref":"#/components/schemas/AuditActor"},"actorIp":{"type":["string","null"]},"onBehalfOf":{"$ref":"#/components/schemas/AuditOnBehalfOf"},"organizationId":{"allOf":[{"$ref":"#/components/schemas/OrganizationId"},{"type":["string","null"]}]},"venueId":{"allOf":[{"$ref":"#/components/schemas/VenueId"},{"type":["string","null"]}]},"action":{"type":"string"},"targetType":{"type":"string"},"targetId":{"type":["string","null"]},"before":{},"after":{},"reason":{"type":["string","null"]},"requestId":{"allOf":[{"$ref":"#/components/schemas/RequestId"},{"type":["string","null"]}]}},"required":["id","occurredAt","recordedAt","actor","actorIp","organizationId","venueId","action","targetType","targetId","reason","requestId"],"description":"One row of the audit log (spec/07 §11)."},"AuditEntryId":{"type":"string","pattern":"^aud_[0-7][0123456789abcdefghjkmnpqrstvwxyz]{25}$","description":"A TypeID: \"aud_\" followed by the base32 form of a UUIDv7.","example":"aud_01k5znw4hmfay8vvr14d2pf2db"},"AuditActor":{"type":"object","properties":{"kind":{"type":"string","enum":["user","member","device","api_key","system"]},"id":{"type":["string","null"]},"label":{"type":["string","null"]}},"required":["kind","id","label"],"description":"Who performed an audited action."},"AuditOnBehalfOf":{"type":"object","properties":{"kind":{"type":"string","enum":["support_grant","impersonation"]},"id":{"type":"string","minLength":1,"maxLength":128},"by":{"type":"string","minLength":1,"maxLength":128}},"required":["kind","id"],"description":"The break-glass grant or impersonation an action was taken under (spec/07 §8)."},"AuditExportRequest":{"type":"object","properties":{"actorId":{"type":"string","minLength":1,"maxLength":128,"description":"Only rows by this actor (a user, device or API key id)."},"actorKind":{"type":"string","enum":["user","member","device","api_key","system"],"description":"Only rows by this kind of actor."},"action":{"type":"string","maxLength":120,"pattern":"^[a-z][a-z0-9_]*(?:\\.[a-z][a-z0-9_]*)*(?:\\.\\*)?$","description":"An action (`sessions.comp`), or every action under a prefix (`sessions.*`)."},"targetType":{"type":"string","minLength":1,"maxLength":64,"pattern":"^[a-z][a-z0-9_]*$","description":"Only rows about this kind of thing: `player`, `setting_value`."},"targetId":{"type":"string","minLength":1,"maxLength":64,"description":"Only rows about this one thing: its uuid or its TypeID. A player’s Audit tab is this filter."},"venueId":{"allOf":[{"$ref":"#/components/schemas/VenueId"},{"description":"Only rows recorded against this venue."}]},"onBehalfOf":{"type":"string","minLength":1,"maxLength":128,"description":"Only rows taken under this break-glass grant or impersonation (spec/07 §8)."},"requestId":{"allOf":[{"$ref":"#/components/schemas/RequestId"},{"description":"Only rows written by this request — the `X-Request-Id` a caller quotes to support."}]},"from":{"allOf":[{"$ref":"#/components/schemas/IsoDateTime"},{"description":"Only rows that occurred at or after this instant."}]},"to":{"allOf":[{"$ref":"#/components/schemas/IsoDateTime"},{"description":"Only rows that occurred before this instant."}]}},"additionalProperties":false},"PlatformAuditExportRequest":{"type":"object","properties":{"actorId":{"type":"string","minLength":1,"maxLength":128,"description":"Only rows by this actor (a user, device or API key id)."},"actorKind":{"type":"string","enum":["user","member","device","api_key","system"],"description":"Only rows by this kind of actor."},"action":{"type":"string","maxLength":120,"pattern":"^[a-z][a-z0-9_]*(?:\\.[a-z][a-z0-9_]*)*(?:\\.\\*)?$","description":"An action (`sessions.comp`), or every action under a prefix (`sessions.*`)."},"targetType":{"type":"string","minLength":1,"maxLength":64,"pattern":"^[a-z][a-z0-9_]*$","description":"Only rows about this kind of thing: `player`, `setting_value`."},"targetId":{"type":"string","minLength":1,"maxLength":64,"description":"Only rows about this one thing: its uuid or its TypeID. A player’s Audit tab is this filter."},"venueId":{"allOf":[{"$ref":"#/components/schemas/VenueId"},{"description":"Only rows recorded against this venue."}]},"onBehalfOf":{"type":"string","minLength":1,"maxLength":128,"description":"Only rows taken under this break-glass grant or impersonation (spec/07 §8)."},"requestId":{"allOf":[{"$ref":"#/components/schemas/RequestId"},{"description":"Only rows written by this request — the `X-Request-Id` a caller quotes to support."}]},"from":{"allOf":[{"$ref":"#/components/schemas/IsoDateTime"},{"description":"Only rows that occurred at or after this instant."}]},"to":{"allOf":[{"$ref":"#/components/schemas/IsoDateTime"},{"description":"Only rows that occurred before this instant."}]},"organizationId":{"allOf":[{"$ref":"#/components/schemas/OrganizationId"},{"description":"Only this organisation’s rows."}]},"reason":{"type":"string","minLength":3,"maxLength":500,"description":"Why you are reading. Recorded, with you, in the log of every organisation whose rows you see."}},"required":["reason"],"additionalProperties":false},"StationPhoneCodeSent":{"type":"object","properties":{"channel":{"$ref":"#/components/schemas/OtpChannel"},"expiresInSeconds":{"type":"integer","exclusiveMinimum":0},"resendAfterSeconds":{"type":"integer","exclusiveMinimum":0}},"required":["channel","expiresInSeconds","resendAfterSeconds"],"description":"A code is on its way to the phone."},"OtpChannel":{"type":"string","enum":["sms","whatsapp"],"description":"How a one-time code is delivered."},"StationPhoneCodeRequest":{"type":"object","properties":{"phoneNumber":{"$ref":"#/components/schemas/PhoneNumber"},"channel":{"$ref":"#/components/schemas/OtpChannel"}},"required":["phoneNumber"]},"StationSignInResult":{"type":"object","properties":{"player":{"$ref":"#/components/schemas/StationSignedInPlayer"},"createdAccount":{"type":"boolean"}},"required":["player","createdAccount"],"description":"The player the station has signed in."},"StationSignedInPlayer":{"type":"object","properties":{"id":{"$ref":"#/components/schemas/PlayerId"}},"required":["id"],"description":"The player a station signed in."},"PlayerId":{"type":"string","pattern":"^ply_[0-7][0123456789abcdefghjkmnpqrstvwxyz]{25}$","description":"A TypeID: \"ply_\" followed by the base32 form of a UUIDv7.","example":"ply_01k5znw4hmfay8vvr14d2pf2db"},"StationPhoneSignIn":{"type":"object","properties":{"phoneNumber":{"$ref":"#/components/schemas/PhoneNumber"},"code":{"$ref":"#/components/schemas/OtpCode"},"consent":{"$ref":"#/components/schemas/SignUpConsent"}},"required":["phoneNumber","code"]},"OtpCode":{"type":"string","pattern":"^\\d{6}$","description":"A 6-digit one-time code.","example":"482913"},"SignUpConsent":{"type":"object","properties":{"terms":{"$ref":"#/components/schemas/LegalDocumentVersion"},"privacy":{"$ref":"#/components/schemas/LegalDocumentVersion"},"marketing":{"type":"boolean"}},"required":["terms","privacy"],"additionalProperties":false,"description":"Terms and privacy (required) and marketing (optional, off by default), captured with the request that creates the account."},"LegalDocumentVersion":{"type":"string","minLength":1,"maxLength":64,"pattern":"^[A-Za-z0-9][A-Za-z0-9._:-]*$","description":"The version of a legal document, as the brand configuration names it.","example":"2026-09-01"},"StationPasswordSignIn":{"type":"object","properties":{"identifier":{"type":"string","minLength":1,"maxLength":254},"password":{"type":"string","minLength":1,"maxLength":256}},"required":["identifier","password"]},"VenueList":{"type":"object","properties":{"data":{"type":"array","items":{"$ref":"#/components/schemas/Venue"}},"page":{"$ref":"#/components/schemas/Page"}},"required":["data","page"]},"Venue":{"type":"object","properties":{"id":{"$ref":"#/components/schemas/VenueId"},"organizationId":{"$ref":"#/components/schemas/OrganizationId"},"name":{"type":"string","minLength":1,"maxLength":120},"slug":{"$ref":"#/components/schemas/VenueSlug"},"timezone":{"$ref":"#/components/schemas/Timezone"},"businessDayCutoff":{"$ref":"#/components/schemas/LocalTime"},"currency":{"$ref":"#/components/schemas/CurrencyCode"},"address":{"allOf":[{"$ref":"#/components/schemas/Address"},{"type":["object","null"]}]},"coordinate":{"allOf":[{"$ref":"#/components/schemas/Coordinate"},{"type":["object","null"]}]},"contact":{"$ref":"#/components/schemas/VenueContact"},"openingHours":{"$ref":"#/components/schemas/OpeningHours"},"holidays":{"$ref":"#/components/schemas/Holidays"},"amenities":{"$ref":"#/components/schemas/VenueAmenities"},"photos":{"$ref":"#/components/schemas/VenuePhotos"},"status":{"$ref":"#/components/schemas/VenueStatus"},"publicListing":{"type":"boolean"},"featured":{"type":"boolean"},"partner":{"type":"boolean"},"sponsored":{"type":"boolean"},"createdAt":{"$ref":"#/components/schemas/IsoDateTime"},"updatedAt":{"$ref":"#/components/schemas/IsoDateTime"},"archivedAt":{"allOf":[{"$ref":"#/components/schemas/IsoDateTime"},{"type":["string","null"]}]}},"required":["id","organizationId","name","slug","timezone","businessDayCutoff","currency","address","coordinate","contact","openingHours","holidays","amenities","photos","status","publicListing","featured","partner","sponsored","createdAt","updatedAt","archivedAt"],"description":"A venue: where it is, when it is open, what it offers (spec/04 §3)."},"VenueSlug":{"type":"string","minLength":3,"maxLength":60,"pattern":"^[a-z0-9]+(?:-[a-z0-9]+)*$","example":"playground-central"},"Timezone":{"type":"string","minLength":1,"maxLength":64,"description":"The IANA timezone name of a place.","example":"Asia/Kolkata"},"VenueContact":{"type":"object","properties":{"phone":{"$ref":"#/components/schemas/PhoneNumber"},"email":{"type":"string","maxLength":254,"format":"email"},"website":{"type":"string","maxLength":2048,"format":"uri","description":"https only."}},"additionalProperties":false,"description":"How players reach the venue. Shown on the public venue page."},"OpeningHours":{"type":"object","properties":{"mon":{"type":"array","items":{"$ref":"#/components/schemas/OpeningInterval"},"maxItems":4},"tue":{"type":"array","items":{"$ref":"#/components/schemas/OpeningInterval"},"maxItems":4},"wed":{"type":"array","items":{"$ref":"#/components/schemas/OpeningInterval"},"maxItems":4},"thu":{"type":"array","items":{"$ref":"#/components/schemas/OpeningInterval"},"maxItems":4},"fri":{"type":"array","items":{"$ref":"#/components/schemas/OpeningInterval"},"maxItems":4},"sat":{"type":"array","items":{"$ref":"#/components/schemas/OpeningInterval"},"maxItems":4},"sun":{"type":"array","items":{"$ref":"#/components/schemas/OpeningInterval"},"maxItems":4}},"required":["mon","tue","wed","thu","fri","sat","sun"],"additionalProperties":false,"description":"The regular week. An empty day is closed all day."},"OpeningInterval":{"type":"object","properties":{"opens":{"$ref":"#/components/schemas/LocalTime"},"closes":{"$ref":"#/components/schemas/LocalTime"}},"required":["opens","closes"],"additionalProperties":false,"description":"Open from `opens` until `closes`, in the venue's timezone. `closes` earlier than `opens` crosses midnight and belongs to the day it opens on; `closes` equal to `opens` is 24 hours."},"Holidays":{"type":"array","items":{"$ref":"#/components/schemas/Holiday"},"maxItems":400},"Holiday":{"type":"object","properties":{"date":{"$ref":"#/components/schemas/LocalDate"},"name":{"type":"string","minLength":1,"maxLength":80},"closed":{"type":"boolean"},"hours":{"type":"array","items":{"$ref":"#/components/schemas/OpeningInterval"},"maxItems":4}},"required":["date","name","closed","hours"],"additionalProperties":false,"description":"A date in the venue's holiday calendar: closed, regular hours (no `hours`), or special hours. `holiday` in a pass or rate window matches it whatever weekday it falls on."},"LocalDate":{"type":"string","format":"date","description":"A date in the venue's timezone.","example":"2026-09-20"},"VenueAmenities":{"type":"array","items":{"$ref":"#/components/schemas/VenueAmenity"},"maxItems":5},"VenueAmenity":{"type":"string","enum":["parking","air_conditioning","food","washrooms","streaming_room"]},"VenuePhotos":{"type":"object","properties":{"cover":{"allOf":[{"$ref":"#/components/schemas/VenuePhoto"},{"type":["object","null"]}]},"gallery":{"type":"array","items":{"$ref":"#/components/schemas/VenuePhoto"},"maxItems":20}},"required":["cover","gallery"],"description":"A cover and a gallery (spec/13 §13)."},"VenueStatus":{"type":"string","enum":["setup","live","paused","closed"],"description":"`setup` before trading, `live`, `paused` (temporarily shut), `closed` (archived for good)."},"CreateVenue":{"type":"object","properties":{"name":{"type":"string","minLength":1,"maxLength":120},"slug":{"$ref":"#/components/schemas/VenueSlug"},"timezone":{"$ref":"#/components/schemas/Timezone"},"currency":{"$ref":"#/components/schemas/CurrencyCode"},"businessDayCutoff":{"$ref":"#/components/schemas/LocalTime"},"address":{"$ref":"#/components/schemas/Address"},"coordinate":{"$ref":"#/components/schemas/Coordinate"},"contact":{"$ref":"#/components/schemas/VenueContact"},"openingHours":{"$ref":"#/components/schemas/OpeningHours"},"holidays":{"$ref":"#/components/schemas/Holidays"},"amenities":{"$ref":"#/components/schemas/VenueAmenities"},"publicListing":{"type":"boolean"}},"required":["name","slug","timezone"],"additionalProperties":false},"UpdateVenue":{"type":"object","properties":{"name":{"type":"string","minLength":1,"maxLength":120},"slug":{"$ref":"#/components/schemas/VenueSlug"},"timezone":{"$ref":"#/components/schemas/Timezone"},"businessDayCutoff":{"$ref":"#/components/schemas/LocalTime"},"address":{"allOf":[{"$ref":"#/components/schemas/Address"},{"type":["object","null"]}]},"coordinate":{"allOf":[{"$ref":"#/components/schemas/Coordinate"},{"type":["object","null"]}]},"contact":{"$ref":"#/components/schemas/VenueContact"},"openingHours":{"$ref":"#/components/schemas/OpeningHours"},"holidays":{"$ref":"#/components/schemas/Holidays"},"amenities":{"$ref":"#/components/schemas/VenueAmenities"},"photos":{"$ref":"#/components/schemas/VenuePhotoRefs"},"status":{"$ref":"#/components/schemas/EditableVenueStatus"},"publicListing":{"type":"boolean"}},"additionalProperties":false},"VenuePhotoRefs":{"type":"object","properties":{"cover":{"allOf":[{"$ref":"#/components/schemas/FileId"},{"type":["string","null"]}]},"gallery":{"type":"array","items":{"$ref":"#/components/schemas/FileId"},"maxItems":20}},"required":["cover","gallery"],"additionalProperties":false},"EditableVenueStatus":{"type":"string","enum":["setup","live","paused"]},"UpdateVenueListing":{"type":"object","properties":{"featured":{"type":"boolean"},"partner":{"type":"boolean"},"sponsored":{"type":"boolean"}},"additionalProperties":false,"description":"HQ discovery flags (spec/14 §3.3)."},"HqVenueList":{"type":"object","properties":{"data":{"type":"array","items":{"$ref":"#/components/schemas/HqVenue"}},"page":{"$ref":"#/components/schemas/Page"}},"required":["data","page"]},"HqVenue":{"type":"object","properties":{"venue":{"$ref":"#/components/schemas/Venue"},"organization":{"$ref":"#/components/schemas/HqVenueOrganization"}},"required":["venue","organization"],"description":"A venue on HQ’s list of every venue, with the organisation it belongs to (spec/14 §3.3)."},"HqVenueOrganization":{"type":"object","properties":{"id":{"$ref":"#/components/schemas/OrganizationId"},"name":{"type":"string"}},"required":["id","name"],"description":"The organisation a venue belongs to, as HQ’s venue list names it."},"PublicVenueList":{"type":"object","properties":{"data":{"type":"array","items":{"$ref":"#/components/schemas/PublicVenue"}},"page":{"$ref":"#/components/schemas/Page"}},"required":["data","page"]},"PublicVenue":{"type":"object","properties":{"id":{"$ref":"#/components/schemas/VenueId"},"slug":{"$ref":"#/components/schemas/VenueSlug"},"name":{"type":"string"},"timezone":{"$ref":"#/components/schemas/Timezone"},"address":{"allOf":[{"$ref":"#/components/schemas/Address"},{"type":["object","null"]}]},"coordinate":{"allOf":[{"$ref":"#/components/schemas/Coordinate"},{"type":["object","null"]}]},"contact":{"$ref":"#/components/schemas/VenueContact"},"openingHours":{"$ref":"#/components/schemas/OpeningHours"},"holidays":{"$ref":"#/components/schemas/Holidays"},"amenities":{"$ref":"#/components/schemas/VenueAmenities"},"photos":{"$ref":"#/components/schemas/VenuePhotos"},"openNow":{"type":"boolean"},"featured":{"type":"boolean"},"partner":{"type":"boolean"},"sponsored":{"type":"boolean"},"distanceMeters":{"type":["integer","null"],"minimum":0}},"required":["id","slug","name","timezone","address","coordinate","contact","openingHours","holidays","amenities","photos","openNow","featured","partner","sponsored","distanceMeters"],"description":"A venue's public listing. A projection, not the venue resource."},"FileUpload":{"type":"object","properties":{"file":{"$ref":"#/components/schemas/File"},"upload":{"$ref":"#/components/schemas/SignedUpload"}},"required":["file","upload"]},"File":{"type":"object","properties":{"id":{"$ref":"#/components/schemas/FileId"},"purpose":{"$ref":"#/components/schemas/FilePurpose"},"venueId":{"allOf":[{"$ref":"#/components/schemas/VenueId"},{"type":["string","null"]}]},"contentType":{"$ref":"#/components/schemas/ImageContentType"},"bytes":{"type":"integer","exclusiveMinimum":0},"status":{"$ref":"#/components/schemas/FileStatus"},"url":{"type":"string","format":"uri"},"variants":{"type":"array","items":{"$ref":"#/components/schemas/ImageVariant"}},"createdAt":{"$ref":"#/components/schemas/IsoDateTime"}},"required":["id","purpose","venueId","contentType","bytes","status","url","variants","createdAt"]},"FilePurpose":{"type":"string","enum":["venue_photo","organization_logo"],"description":"`venue_photo`: a venue’s photo. `organization_logo`: an organisation’s logo for its venues’ lock screens and receipts."},"FileStatus":{"type":"string","enum":["pending","uploaded","ready","failed"],"description":"`pending` until the upload is completed, `uploaded` once the server has checked it, `ready` once the worker has made its variants, `failed` if the object was missing or not what was declared — or if the upload was still not completed an hour after it started, when it is removed unchecked."},"SignedUpload":{"type":"object","properties":{"method":{"type":"string","enum":["PUT"]},"url":{"type":"string","format":"uri"},"contentType":{"$ref":"#/components/schemas/ImageContentType"},"expiresAt":{"$ref":"#/components/schemas/IsoDateTime"}},"required":["method","url","contentType","expiresAt"]},"CreateFile":{"type":"object","properties":{"purpose":{"$ref":"#/components/schemas/FilePurpose"},"venueId":{"$ref":"#/components/schemas/VenueId"},"contentType":{"$ref":"#/components/schemas/ImageContentType"},"bytes":{"type":"integer","exclusiveMinimum":0,"maximum":10485760}},"required":["purpose","contentType","bytes"],"additionalProperties":false},"StationClassList":{"type":"object","properties":{"data":{"type":"array","items":{"$ref":"#/components/schemas/StationClass"}},"page":{"$ref":"#/components/schemas/Page"}},"required":["data","page"]},"StationClass":{"type":"object","properties":{"id":{"$ref":"#/components/schemas/StationClassId"},"organizationId":{"$ref":"#/components/schemas/OrganizationId"},"venueId":{"$ref":"#/components/schemas/VenueId"},"name":{"type":"string","minLength":1,"maxLength":60},"colorToken":{"$ref":"#/components/schemas/ColorToken"},"description":{"type":"string"},"ageLimit":{"type":["integer","null"],"minimum":1,"maximum":21,"description":"The youngest a player may be to start a session here, on top of the venue’s minimum age."},"pricingMode":{"$ref":"#/components/schemas/PricingMode"},"defaultCapacity":{"type":"integer","minimum":1,"maximum":16},"createdAt":{"$ref":"#/components/schemas/IsoDateTime"},"updatedAt":{"$ref":"#/components/schemas/IsoDateTime"}},"required":["id","organizationId","venueId","name","colorToken","description","ageLimit","pricingMode","defaultCapacity","createdAt","updatedAt"],"description":"A price and permission tier of stations (spec/04 §3)."},"StationClassId":{"type":"string","pattern":"^scl_[0-7][0123456789abcdefghjkmnpqrstvwxyz]{25}$","description":"A TypeID: \"scl_\" followed by the base32 form of a UUIDv7.","example":"scl_01k5znw4hmfay8vvr14d2pf2db"},"ColorToken":{"type":"string","enum":["ink","muted","line-strong","ok-ink","warn-ink","danger-ink","accent-ink","focus","danger"],"description":"A colour design token (spec/BRAND.md §4), never a hex: the colour tokens that stay visible on every ground in every theme.","example":"accent-ink"},"PricingMode":{"type":"string","enum":["per_player","per_station"],"description":"`per_player`: each player on a station pays. `per_station`: the station is priced once, whoever plays."},"CreateStationClass":{"type":"object","properties":{"name":{"type":"string","minLength":1,"maxLength":60},"colorToken":{"$ref":"#/components/schemas/ColorToken"},"description":{"type":"string","maxLength":500,"description":"Shown to players: what this class is and why it costs what it does."},"ageLimit":{"type":["integer","null"],"minimum":1,"maximum":21,"description":"The youngest a player may be to start a session here, on top of the venue’s minimum age."},"pricingMode":{"$ref":"#/components/schemas/PricingMode"},"defaultCapacity":{"type":"integer","minimum":1,"maximum":16}},"required":["name","colorToken","pricingMode"],"additionalProperties":false},"UpdateStationClass":{"type":"object","properties":{"name":{"type":"string","minLength":1,"maxLength":60},"colorToken":{"$ref":"#/components/schemas/ColorToken"},"description":{"type":"string","maxLength":500,"description":"Shown to players: what this class is and why it costs what it does."},"ageLimit":{"type":["integer","null"],"minimum":1,"maximum":21,"description":"The youngest a player may be to start a session here, on top of the venue’s minimum age."},"pricingMode":{"$ref":"#/components/schemas/PricingMode"},"defaultCapacity":{"type":"integer","minimum":1,"maximum":16}},"additionalProperties":false},"ZoneList":{"type":"object","properties":{"data":{"type":"array","items":{"$ref":"#/components/schemas/Zone"}},"page":{"$ref":"#/components/schemas/Page"}},"required":["data","page"]},"Zone":{"type":"object","properties":{"id":{"$ref":"#/components/schemas/ZoneId"},"organizationId":{"$ref":"#/components/schemas/OrganizationId"},"venueId":{"$ref":"#/components/schemas/VenueId"},"name":{"type":"string","minLength":1,"maxLength":60},"colorToken":{"$ref":"#/components/schemas/ColorToken"},"floor":{"$ref":"#/components/schemas/FloorNumber"},"createdAt":{"$ref":"#/components/schemas/IsoDateTime"},"updatedAt":{"$ref":"#/components/schemas/IsoDateTime"}},"required":["id","organizationId","venueId","name","colorToken","floor","createdAt","updatedAt"],"description":"A named area of the floor (spec/04 §3). Its rectangle is on the floor plan."},"ZoneId":{"type":"string","pattern":"^zon_[0-7][0123456789abcdefghjkmnpqrstvwxyz]{25}$","description":"A TypeID: \"zon_\" followed by the base32 form of a UUIDv7.","example":"zon_01k5znw4hmfay8vvr14d2pf2db"},"FloorNumber":{"type":"integer","minimum":0,"maximum":9,"description":"Which level of the venue: 0 is the ground floor.","example":0},"CreateZone":{"type":"object","properties":{"name":{"type":"string","minLength":1,"maxLength":60},"colorToken":{"$ref":"#/components/schemas/ColorToken"},"floor":{"$ref":"#/components/schemas/FloorNumber"}},"required":["name","colorToken"],"additionalProperties":false},"UpdateZone":{"type":"object","properties":{"name":{"type":"string","minLength":1,"maxLength":60},"colorToken":{"$ref":"#/components/schemas/ColorToken"}},"additionalProperties":false},"FloorPlan":{"type":"object","properties":{"venueId":{"$ref":"#/components/schemas/VenueId"},"floor":{"$ref":"#/components/schemas/FloorNumber"},"columns":{"type":"integer","minimum":1,"maximum":60},"rows":{"type":"integer","minimum":1,"maximum":100},"zones":{"type":"array","items":{"$ref":"#/components/schemas/FloorZone"},"maxItems":30},"fixtures":{"type":"array","items":{"$ref":"#/components/schemas/FloorFixture"},"maxItems":100},"version":{"type":"integer","minimum":0},"updatedAt":{"allOf":[{"$ref":"#/components/schemas/IsoDateTime"},{"type":["string","null"]}]}},"required":["venueId","floor","columns","rows","zones","fixtures","version","updatedAt"],"description":"One floor of a venue: its grid, its zones’ rectangles and its fixtures."},"FloorZone":{"type":"object","properties":{"zoneId":{"$ref":"#/components/schemas/ZoneId"},"x":{"type":"integer","minimum":0,"maximum":59},"y":{"type":"integer","minimum":0,"maximum":99},"w":{"type":"integer","minimum":1,"maximum":60},"h":{"type":"integer","minimum":1,"maximum":100}},"required":["zoneId","x","y","w","h"],"description":"A zone’s rectangle on the plan. Each zone appears once, on its own floor."},"FloorFixture":{"type":"object","properties":{"kind":{"$ref":"#/components/schemas/FixtureKind"},"label":{"type":["string","null"],"maxLength":40},"x":{"type":"integer","minimum":0,"maximum":59},"y":{"type":"integer","minimum":0,"maximum":99},"w":{"type":"integer","minimum":1,"maximum":60},"h":{"type":"integer","minimum":1,"maximum":100}},"required":["kind","label","x","y","w","h"],"description":"Something on the floor that is not a station: a counter, a wall, the stairs."},"FixtureKind":{"type":"string","enum":["counter","entrance","lounge","wall","pillar","screen","window","restroom","stairs"]},"PutFloorPlan":{"type":"object","properties":{"columns":{"type":"integer","minimum":1,"maximum":60},"rows":{"type":"integer","minimum":1,"maximum":100},"zones":{"type":"array","items":{"$ref":"#/components/schemas/FloorZone"},"maxItems":6000},"fixtures":{"type":"array","items":{"$ref":"#/components/schemas/FloorFixture"},"maxItems":6000}},"required":["columns","rows","zones","fixtures"],"additionalProperties":false},"SettingsView":{"type":"object","properties":{"scope":{"$ref":"#/components/schemas/SettingsViewScope"},"groups":{"type":"array","items":{"$ref":"#/components/schemas/SettingGroupHeading"}},"settings":{"type":"array","items":{"$ref":"#/components/schemas/SettingEntry"}}},"required":["scope","groups","settings"],"description":"A generated settings screen: every setting it shows, and each one’s state."},"SettingsViewScope":{"type":"object","properties":{"type":{"type":"string","enum":["platform","organization","venue"]},"organizationId":{"allOf":[{"$ref":"#/components/schemas/OrganizationId"},{"type":["string","null"]}]},"venueId":{"allOf":[{"$ref":"#/components/schemas/VenueId"},{"type":["string","null"]}]}},"required":["type","organizationId","venueId"],"description":"The scope a settings screen edits."},"SettingGroupHeading":{"type":"object","properties":{"id":{"$ref":"#/components/schemas/SettingGroup"},"title":{"type":"string"}},"required":["id","title"],"description":"A heading on a settings screen, in the order to show them."},"SettingGroup":{"type":"string","enum":["venueProfile","kiosk","sessions","stationsAndLocker","pricing","payments","taxAndInvoices","foodAndDrinks","reservations","loyalty","players","printersAndHardware","notifications","integrations","staff","dataRetention","featureFlags"],"description":"The heading a settings screen shows a setting under (spec/13 §13)."},"SettingEntry":{"type":"object","properties":{"key":{"$ref":"#/components/schemas/SettingKey"},"group":{"$ref":"#/components/schemas/SettingGroup"},"description":{"type":"string"},"schema":{"type":"object","additionalProperties":{}},"default":{},"scopes":{"type":"array","items":{"$ref":"#/components/schemas/SettingScope"}},"visibleTo":{"$ref":"#/components/schemas/SettingAudience"},"editPermission":{"$ref":"#/components/schemas/Permission"},"entitlement":{"$ref":"#/components/schemas/EntitlementKey"},"value":{},"source":{"$ref":"#/components/schemas/SettingSource"},"inherited":{"$ref":"#/components/schemas/InheritedSettingValue"},"hasOverride":{"type":"boolean"},"lockedAt":{"$ref":"#/components/schemas/SettingLockScope"},"lockableAt":{"type":"array","items":{"$ref":"#/components/schemas/SettingLockScope"}},"editable":{"type":"boolean"},"editableScopes":{"type":"array","items":{"$ref":"#/components/schemas/SettingScope"}},"overrides":{"type":"array","items":{"$ref":"#/components/schemas/PlaceSettingValue"}}},"required":["key","group","description","schema","scopes","visibleTo","editPermission","source","inherited","hasOverride","lockableAt","editable","editableScopes","overrides"],"description":"One setting as a settings screen shows it (spec/13 §13)."},"SettingKey":{"type":"string","maxLength":120,"pattern":"^[a-z][a-z0-9]*(?:\\.[a-z][a-zA-Z0-9]*)+$","description":"A key in the settings registry.","example":"sessions.offlinePolicy"},"SettingScope":{"type":"string","enum":["platform","organization","venue","zone","stationClass","station"],"description":"Where a value may be set. Resolution runs station → station class → zone → venue → organisation → platform."},"SettingAudience":{"type":"string","enum":["platform","organization","venue"],"description":"The lowest settings screen that shows the setting: HQ’s, the organisation’s or the venue’s."},"Permission":{"type":"string","pattern":"^[a-z][a-z0-9_]*:[a-z][a-z0-9_]*$","description":"A permission, written resource:action.","example":"sessions:end"},"EntitlementKey":{"type":"string","enum":["fnb","inventory","manualStations","wallet","memberships","reservations","loyaltyVenueRewards","apiAccess","webhooks","maxVenues","maxStations","remoteControl","advancedReports"],"description":"A subscription entitlement (spec/14 §4.1)."},"SettingSource":{"type":"string","enum":["default","platform","organization","venue"],"description":"Where a value comes from: the registry default, or the value stored at a scope."},"InheritedSettingValue":{"type":"object","properties":{"value":{},"source":{"$ref":"#/components/schemas/SettingSource"}},"required":["source"],"description":"What \"reset to inherited\" would give, and where it comes from."},"SettingLockScope":{"type":"string","enum":["platform","organization"],"description":"Where HQ may lock a setting so that no scope below can override it."},"PlaceSettingValue":{"type":"object","properties":{"scopeType":{"type":"string","enum":["stationClass","zone"]},"stationClassId":{"allOf":[{"$ref":"#/components/schemas/StationClassId"},{"type":["string","null"]}]},"zoneId":{"allOf":[{"$ref":"#/components/schemas/ZoneId"},{"type":["string","null"]}]},"value":{},"updatedAt":{"$ref":"#/components/schemas/IsoDateTime"}},"required":["scopeType","stationClassId","zoneId","updatedAt"],"description":"A value stored for one station class or zone of the venue. It applies to the stations there — unless a lock above the venue binds them."},"PutLockableSettings":{"type":"object","properties":{"changes":{"type":"array","items":{"$ref":"#/components/schemas/LockableSettingChange"},"minItems":1,"maxItems":50}},"required":["changes"],"additionalProperties":false},"LockableSettingChange":{"type":"object","properties":{"key":{"$ref":"#/components/schemas/SettingKey"},"value":{},"reset":{"type":"boolean"},"locked":{"type":"boolean"}},"required":["key"],"additionalProperties":false,"description":"Set, lock, unlock or reset a value (HQ)."},"PutSettings":{"type":"object","properties":{"changes":{"type":"array","items":{"$ref":"#/components/schemas/SettingChange"},"minItems":1,"maxItems":50}},"required":["changes"],"additionalProperties":false},"SettingChange":{"type":"object","properties":{"key":{"$ref":"#/components/schemas/SettingKey"},"value":{},"reset":{"type":"boolean"}},"required":["key"],"additionalProperties":false,"description":"Set a value at this scope, or reset it to the inherited one."},"PutVenueSettings":{"type":"object","properties":{"changes":{"type":"array","items":{"$ref":"#/components/schemas/VenueSettingChange"},"minItems":1,"maxItems":50}},"required":["changes"],"additionalProperties":false},"VenueSettingChange":{"type":"object","properties":{"key":{"$ref":"#/components/schemas/SettingKey"},"value":{},"reset":{"type":"boolean"},"stationClassId":{"$ref":"#/components/schemas/StationClassId"},"zoneId":{"$ref":"#/components/schemas/ZoneId"}},"required":["key"],"additionalProperties":false,"description":"Set or reset a value for the venue, or for one of its station classes or zones."},"MyProfile":{"type":"object","properties":{"id":{"$ref":"#/components/schemas/PlayerId"},"username":{"type":["string","null"]},"displayName":{"type":["string","null"]},"marketingOptIn":{"type":"boolean"},"email":{"type":["string","null"]},"emailVerified":{"type":"boolean"},"phoneNumber":{"type":["string","null"]},"phoneNumberVerified":{"type":"boolean"}},"required":["id","username","displayName","marketingOptIn","email","emailVerified","phoneNumber","phoneNumberVerified"],"description":"The signed-in player's profile."},"ContactVerificationStarted":{"type":"object","properties":{"contact":{"type":"string","enum":["email","phone"]},"channel":{"type":"string","enum":["email","sms","whatsapp"]},"destination":{"type":"string"},"expiresAt":{"$ref":"#/components/schemas/IsoDateTime"},"profile":{"$ref":"#/components/schemas/MyProfile"}},"required":["contact","channel","destination","expiresAt","profile"],"description":"A code is on its way to the new email address or phone number; nothing has changed yet."},"UpdateMe":{"type":"object","properties":{"marketingOptIn":{"type":"boolean"},"email":{"type":"string","minLength":3,"maxLength":254,"pattern":"^[^\\s@]+@[^\\s@]+\\.[^\\s@]+$"},"phoneNumber":{"$ref":"#/components/schemas/PhoneNumber"}},"additionalProperties":false},"MyConsents":{"type":"object","properties":{"terms":{"$ref":"#/components/schemas/AcceptedDocument"},"privacy":{"$ref":"#/components/schemas/AcceptedDocument"},"marketing":{"$ref":"#/components/schemas/MarketingConsent"},"history":{"type":"array","items":{"$ref":"#/components/schemas/ConsentRecord"}}},"required":["marketing","history"],"description":"What the signed-in player has agreed to, and the full record of it."},"AcceptedDocument":{"type":"object","properties":{"version":{"$ref":"#/components/schemas/LegalDocumentVersion"},"acceptedAt":{"$ref":"#/components/schemas/IsoDateTime"}},"required":["version","acceptedAt"],"description":"The latest version of a document the player accepted, and when."},"MarketingConsent":{"type":"object","properties":{"optIn":{"type":"boolean"},"grantedAt":{"$ref":"#/components/schemas/IsoDateTime"},"version":{"$ref":"#/components/schemas/LegalDocumentVersion"}},"required":["optIn"],"description":"Whether the player has opted in to marketing, and since when."},"ConsentRecord":{"type":"object","properties":{"id":{"$ref":"#/components/schemas/ConsentId"},"kind":{"$ref":"#/components/schemas/ConsentKind"},"version":{"$ref":"#/components/schemas/LegalDocumentVersion"},"grantedAt":{"$ref":"#/components/schemas/IsoDateTime"},"revokedAt":{"$ref":"#/components/schemas/IsoDateTime"},"source":{"$ref":"#/components/schemas/ConsentSource"}},"required":["id","kind","version","grantedAt","source"],"description":"One consent row: what was agreed to, which version, when, and how."},"ConsentId":{"type":"string","pattern":"^cns_[0-7][0123456789abcdefghjkmnpqrstvwxyz]{25}$","description":"A TypeID: \"cns_\" followed by the base32 form of a UUIDv7.","example":"cns_01k5znw4hmfay8vvr14d2pf2db"},"ConsentKind":{"type":"string","enum":["terms","privacy","marketing"],"description":"What was agreed to: the terms, the privacy policy, or marketing."},"ConsentSource":{"type":"object","properties":{"flow":{"type":"string","enum":["sign_up","profile","notification_prefs","document_acceptance"]},"method":{"type":"string","description":"How the account was created, for a sign-up consent: one of phone_otp, email_password, google, discord, steam, and more as sign-in methods are added.","example":"phone_otp"},"surface":{"type":"string","enum":["kiosk"]},"organizationId":{"$ref":"#/components/schemas/OrganizationId"},"venueId":{"$ref":"#/components/schemas/VenueId"},"stationId":{"$ref":"#/components/schemas/StationId"}},"required":["flow"],"description":"How and where a consent was given."},"StationId":{"type":"string","pattern":"^stn_[0-7][0123456789abcdefghjkmnpqrstvwxyz]{25}$","description":"A TypeID: \"stn_\" followed by the base32 form of a UUIDv7.","example":"stn_01k5znw4hmfay8vvr14d2pf2db"},"AcceptConsents":{"type":"object","properties":{"terms":{"$ref":"#/components/schemas/LegalDocumentVersion"},"privacy":{"$ref":"#/components/schemas/LegalDocumentVersion"}},"additionalProperties":false},"NotificationPrefs":{"type":"object","properties":{"marketing":{"$ref":"#/components/schemas/MarketingConsent"}},"required":["marketing"],"description":"The signed-in person's notification preferences. Categories, channels, quiet hours and venue mutes are added by F-NTF-01."},"PutNotificationPrefs":{"type":"object","properties":{"marketing":{"$ref":"#/components/schemas/MarketingChoice"}},"required":["marketing"],"additionalProperties":false},"MarketingChoice":{"type":"object","properties":{"optIn":{"type":"boolean"}},"required":["optIn"],"additionalProperties":false,"description":"Marketing on or off."},"MemberList":{"type":"object","properties":{"data":{"type":"array","items":{"$ref":"#/components/schemas/Member"}},"page":{"$ref":"#/components/schemas/Page"}},"required":["data","page"]},"Member":{"type":"object","properties":{"id":{"$ref":"#/components/schemas/MemberId"},"organizationId":{"$ref":"#/components/schemas/OrganizationId"},"role":{"$ref":"#/components/schemas/RoleName"},"venueIds":{"$ref":"#/components/schemas/VenueScope"},"status":{"$ref":"#/components/schemas/MemberStatus"},"suspendedAt":{"allOf":[{"$ref":"#/components/schemas/IsoDateTime"},{"type":["string","null"]}]},"name":{"type":["string","null"]},"phoneNumber":{"type":["string","null"]},"email":{"type":["string","null"]},"secondFactorEnabled":{"type":"boolean"},"joinedAt":{"$ref":"#/components/schemas/IsoDateTime"}},"required":["id","organizationId","role","venueIds","status","suspendedAt","name","phoneNumber","email","secondFactorEnabled","joinedAt"],"description":"A person on the staff of one organisation (spec/07 §6.1)."},"VenueScope":{"type":"array","items":{"$ref":"#/components/schemas/VenueId"},"maxItems":100,"description":"Venue ids this member may work at. Empty: every venue of the organisation."},"MemberStatus":{"type":"string","enum":["active","suspended"],"description":"`suspended`: no staff access until reinstated; the role and venues are kept."},"ChangeMemberRole":{"type":"object","properties":{"role":{"$ref":"#/components/schemas/RoleName"}},"required":["role"]},"ChangeMemberVenues":{"type":"object","properties":{"venueIds":{"$ref":"#/components/schemas/VenueScope"}},"required":["venueIds"]},"CreatedInvitation":{"type":"object","properties":{"id":{"$ref":"#/components/schemas/InvitationId"},"organizationId":{"$ref":"#/components/schemas/OrganizationId"},"channel":{"$ref":"#/components/schemas/InvitationChannel"},"destination":{"type":"string"},"role":{"$ref":"#/components/schemas/RoleName"},"venueIds":{"$ref":"#/components/schemas/VenueScope"},"status":{"$ref":"#/components/schemas/InvitationStatus"},"expiresAt":{"$ref":"#/components/schemas/IsoDateTime"},"createdAt":{"$ref":"#/components/schemas/IsoDateTime"},"acceptUrl":{"type":"string"},"delivery":{"$ref":"#/components/schemas/InvitationDelivery"}},"required":["id","organizationId","channel","destination","role","venueIds","status","expiresAt","createdAt","acceptUrl","delivery"],"description":"An invitation just made, with its link and whether its message went."},"InvitationId":{"type":"string","pattern":"^inv_[0-7][0123456789abcdefghjkmnpqrstvwxyz]{25}$","description":"A TypeID: \"inv_\" followed by the base32 form of a UUIDv7.","example":"inv_01k5znw4hmfay8vvr14d2pf2db"},"InvitationChannel":{"type":"string","enum":["phone","email"]},"InvitationStatus":{"type":"string","enum":["pending","accepted","revoked","expired"]},"InvitationDelivery":{"type":"object","properties":{"status":{"type":"string","enum":["sent","not_sent"]},"channel":{"$ref":"#/components/schemas/InvitationMessageChannel"}},"required":["status"],"description":"Whether the invitation message (N-25) was sent."},"InvitationMessageChannel":{"type":"string","enum":["sms","whatsapp","email"]},"CreateInvitation":{"type":"object","properties":{"phoneNumber":{"$ref":"#/components/schemas/PhoneNumber"},"email":{"type":"string","minLength":3,"maxLength":254,"pattern":"^[^\\s@]+@[^\\s@]+$"},"role":{"$ref":"#/components/schemas/RoleName"},"venueIds":{"$ref":"#/components/schemas/VenueScope"}},"required":["role"]},"InvitationList":{"type":"object","properties":{"data":{"type":"array","items":{"$ref":"#/components/schemas/Invitation"}},"page":{"$ref":"#/components/schemas/Page"}},"required":["data","page"]},"Invitation":{"type":"object","properties":{"id":{"$ref":"#/components/schemas/InvitationId"},"organizationId":{"$ref":"#/components/schemas/OrganizationId"},"channel":{"$ref":"#/components/schemas/InvitationChannel"},"destination":{"type":"string"},"role":{"$ref":"#/components/schemas/RoleName"},"venueIds":{"$ref":"#/components/schemas/VenueScope"},"status":{"$ref":"#/components/schemas/InvitationStatus"},"expiresAt":{"$ref":"#/components/schemas/IsoDateTime"},"createdAt":{"$ref":"#/components/schemas/IsoDateTime"}},"required":["id","organizationId","channel","destination","role","venueIds","status","expiresAt","createdAt"],"description":"An invitation to join the staff (spec/07 §6.5)."},"AcceptedInvitation":{"type":"object","properties":{"organizationId":{"$ref":"#/components/schemas/OrganizationId"},"member":{"$ref":"#/components/schemas/Member"}},"required":["organizationId","member"],"description":"The membership the invitation created."},"AcceptInvitation":{"type":"object","properties":{"token":{"type":"string","minLength":20,"maxLength":512}},"required":["token"]},"PlayerBan":{"type":"object","properties":{"id":{"$ref":"#/components/schemas/PlayerBanId"},"organizationId":{"$ref":"#/components/schemas/OrganizationId"},"scope":{"$ref":"#/components/schemas/PlayerBanScope"},"venueId":{"allOf":[{"$ref":"#/components/schemas/VenueId"},{"type":["string","null"]}]},"playerId":{"$ref":"#/components/schemas/PlayerId"},"reason":{"type":"string"},"expiresAt":{"allOf":[{"$ref":"#/components/schemas/IsoDateTime"},{"type":["string","null"]}]},"status":{"$ref":"#/components/schemas/PlayerBanStatus"},"bannedAt":{"$ref":"#/components/schemas/IsoDateTime"},"updatedAt":{"$ref":"#/components/schemas/IsoDateTime"},"liftedAt":{"allOf":[{"$ref":"#/components/schemas/IsoDateTime"},{"type":["string","null"]}]},"liftReason":{"type":["string","null"]}},"required":["id","organizationId","scope","venueId","playerId","reason","expiresAt","status","bannedAt","updatedAt","liftedAt","liftReason"],"description":"A player’s ban at one venue or across one organisation (spec/07 §3.5)."},"PlayerBanId":{"type":"string","pattern":"^ban_[0-7][0123456789abcdefghjkmnpqrstvwxyz]{25}$","description":"A TypeID: \"ban_\" followed by the base32 form of a UUIDv7.","example":"ban_01k5znw4hmfay8vvr14d2pf2db"},"PlayerBanScope":{"type":"string","enum":["venue","organization"]},"PlayerBanStatus":{"type":"string","enum":["active","expired","lifted"]},"BanPlayer":{"type":"object","properties":{"reason":{"$ref":"#/components/schemas/BanReason"},"expiresAt":{"allOf":[{"$ref":"#/components/schemas/IsoDateTime"},{"type":["string","null"]}]}},"required":["reason"]},"BanReason":{"type":"string","minLength":1,"maxLength":500,"description":"Why, for the staff and for the audit log. Internal: never shown to the player or on a kiosk."},"LiftBan":{"type":"object","properties":{"reason":{"$ref":"#/components/schemas/BanReason"}},"required":["reason"]},"PlayerBanList":{"type":"object","properties":{"data":{"type":"array","items":{"$ref":"#/components/schemas/PlayerBan"}},"page":{"$ref":"#/components/schemas/Page"}},"required":["data","page"]},"PlatformBan":{"type":"object","properties":{"playerId":{"$ref":"#/components/schemas/PlayerId"},"banned":{"type":"boolean"},"reason":{"type":["string","null"]},"expiresAt":{"allOf":[{"$ref":"#/components/schemas/IsoDateTime"},{"type":["string","null"]}]}},"required":["playerId","banned","reason","expiresAt"],"description":"Whether a person is banned from the Platform (spec/07 §3.5)."},"HqPlayerList":{"type":"object","properties":{"data":{"type":"array","items":{"$ref":"#/components/schemas/HqPlayerSummary"}},"page":{"$ref":"#/components/schemas/Page"}},"required":["data","page"]},"HqPlayerSummary":{"type":"object","properties":{"id":{"$ref":"#/components/schemas/PlayerId"},"username":{"type":["string","null"]},"displayName":{"type":["string","null"]},"firstName":{"type":["string","null"]},"lastName":{"type":["string","null"]},"phoneNumber":{"type":["string","null"]},"email":{"type":["string","null"]},"contactMasked":{"type":"boolean"},"banned":{"type":"boolean"},"createdAt":{"$ref":"#/components/schemas/IsoDateTime"}},"required":["id","username","displayName","firstName","lastName","phoneNumber","email","contactMasked","banned","createdAt"],"description":"A player as HQ’s search finds them: platform-level data only (spec/14 §10)."},"HqPlayer":{"type":"object","properties":{"id":{"$ref":"#/components/schemas/PlayerId"},"username":{"type":["string","null"]},"displayName":{"type":["string","null"]},"firstName":{"type":["string","null"]},"lastName":{"type":["string","null"]},"phoneNumber":{"type":["string","null"]},"email":{"type":["string","null"]},"contactMasked":{"type":"boolean"},"banned":{"type":"boolean"},"createdAt":{"$ref":"#/components/schemas/IsoDateTime"},"phoneNumberVerified":{"type":"boolean"},"emailVerified":{"type":"boolean"},"signInMethods":{"type":"array","items":{"$ref":"#/components/schemas/HqPlayerSignInMethod"}},"twoFactorEnabled":{"type":"boolean"},"devices":{"type":"array","items":{"$ref":"#/components/schemas/HqPlayerDevice"}},"platformBan":{"$ref":"#/components/schemas/PlatformBan"}},"required":["id","username","displayName","firstName","lastName","phoneNumber","email","contactMasked","banned","createdAt","phoneNumberVerified","emailVerified","signInMethods","twoFactorEnabled","devices","platformBan"],"description":"One player’s platform record as HQ sees it: the profile, how they sign in, their devices and the platform ban (spec/14 §10). No organisation’s dealings with them."},"HqPlayerSignInMethod":{"type":"string","enum":["phone","email","password","google","discord","steam"]},"HqPlayerDevice":{"type":"object","properties":{"deviceKey":{"type":"string"},"firstSeenAt":{"$ref":"#/components/schemas/IsoDateTime"},"lastSeenAt":{"$ref":"#/components/schemas/IsoDateTime"}},"required":["deviceKey","firstSeenAt","lastSeenAt"],"description":"A device the person has signed in on."},"ChangeMemberStatus":{"type":"object","properties":{"status":{"$ref":"#/components/schemas/MemberStatus"}},"required":["status"]},"Role":{"type":"object","properties":{"name":{"$ref":"#/components/schemas/RoleName"},"builtIn":{"type":"boolean"},"permissions":{"type":"array","items":{"$ref":"#/components/schemas/Permission"}},"limits":{"$ref":"#/components/schemas/RoleLimits"}},"required":["name","builtIn","permissions","limits"],"description":"An organisation role: its permissions and its limits (spec/07 §6.2–§6.4)."},"RoleLimits":{"type":"object","properties":{"compMaxMinutesPerAction":{"type":["integer","null"],"minimum":0,"maximum":1440},"compMaxMinutesPerShift":{"type":["integer","null"],"minimum":0,"maximum":1440},"manualDiscountMaxBps":{"type":["integer","null"],"minimum":0,"maximum":10000},"refundApprovalAmount":{"allOf":[{"$ref":"#/components/schemas/NonNegativeMoney"},{"type":["object","null"]}]},"walletAdjustMaxAmount":{"allOf":[{"$ref":"#/components/schemas/NonNegativeMoney"},{"type":["object","null"]}]}},"required":["compMaxMinutesPerAction","compMaxMinutesPerShift","manualDiscountMaxBps","refundApprovalAmount","walletAdjustMaxAmount"],"additionalProperties":false,"description":"The numbers a role may not exceed (spec/07 §6.4). `null` is no limit of the role’s own: the setting alone applies. The lower of the two always wins."},"IdCheck":{"type":"object","properties":{"id":{"$ref":"#/components/schemas/IdCheckId"},"organizationId":{"$ref":"#/components/schemas/OrganizationId"},"playerId":{"$ref":"#/components/schemas/PlayerId"},"method":{"$ref":"#/components/schemas/IdCheckMethod"},"document":{"allOf":[{"$ref":"#/components/schemas/IdCheckDocument"},{"type":["object","null"]}]},"verifiedBy":{"allOf":[{"$ref":"#/components/schemas/IdCheckVerifier"},{"type":["object","null"]}]},"verifiedAt":{"$ref":"#/components/schemas/IsoDateTime"},"photo":{"allOf":[{"$ref":"#/components/schemas/IdCheckPhoto"},{"type":["object","null"]}]}},"required":["id","organizationId","playerId","method","document","verifiedBy","verifiedAt","photo"],"description":"One verification of a player, as this organisation sees it: its own staff document checks. Never another organisation’s (spec/07 §10)."},"IdCheckId":{"type":"string","pattern":"^idc_[0-7][0123456789abcdefghjkmnpqrstvwxyz]{25}$","description":"A TypeID: \"idc_\" followed by the base32 form of a UUIDv7.","example":"idc_01k5znw4hmfay8vvr14d2pf2db"},"IdCheckMethod":{"type":"string","enum":["digital","staff_document"]},"IdempotencyKey":{"type":"string","format":"uuid","description":"Client-generated UUID, unique per logical create."},"RecordIdCheck":{"type":"object","properties":{"documentType":{"$ref":"#/components/schemas/IdDocumentType"},"documentLast4":{"$ref":"#/components/schemas/DocumentLast4"},"photo":{"$ref":"#/components/schemas/IdCheckPhotoUpload"}},"required":["documentType","documentLast4"],"additionalProperties":false,"description":"What the staff member saw: the document type and the last four characters of its number. Nothing else of the number is accepted — an unknown field is refused, not ignored."},"DocumentLast4":{"type":"string","pattern":"^[A-Za-z0-9]{4}$","description":"The last four characters of the document number. The full number is never sent or stored.","example":"4821"},"IdCheckPhotoUpload":{"type":"object","properties":{"contentType":{"$ref":"#/components/schemas/ImageContentType"},"data":{"type":"string","minLength":1,"maxLength":6990508}},"required":["contentType","data"],"additionalProperties":false,"description":"A photo of the document, at most 5242880 bytes before encoding."},"IdCheckList":{"type":"object","properties":{"data":{"type":"array","items":{"$ref":"#/components/schemas/IdCheck"}},"page":{"$ref":"#/components/schemas/Page"}},"required":["data","page"]},"IdCheckPhotoLink":{"type":"object","properties":{"url":{"type":"string","format":"uri"},"expiresAt":{"$ref":"#/components/schemas/IsoDateTime"},"contentType":{"$ref":"#/components/schemas/ImageContentType"}},"required":["url","expiresAt","contentType"],"description":"A short-lived signed URL to one ID photo."},"RoleList":{"type":"object","properties":{"data":{"type":"array","items":{"$ref":"#/components/schemas/Role"}}},"required":["data"],"description":"Every role of the organisation: the built-in ones and its custom roles."},"CreateRole":{"type":"object","properties":{"name":{"$ref":"#/components/schemas/RoleName"},"permissions":{"type":"array","items":{"$ref":"#/components/schemas/Permission"},"minItems":1,"maxItems":200}},"required":["name","permissions"],"additionalProperties":false,"description":"A new custom role: its name and the permissions it combines."},"UpdateRole":{"type":"object","properties":{"permissions":{"type":"array","items":{"$ref":"#/components/schemas/Permission"},"minItems":1,"maxItems":200}},"required":["permissions"],"additionalProperties":false,"description":"A custom role’s new permission set."},"MyAccess":{"type":"object","properties":{"scope":{"$ref":"#/components/schemas/AccessScope"},"organizationId":{"allOf":[{"$ref":"#/components/schemas/OrganizationId"},{"type":["string","null"]}]},"venueId":{"allOf":[{"$ref":"#/components/schemas/VenueId"},{"type":["string","null"]}]},"memberId":{"allOf":[{"$ref":"#/components/schemas/MemberId"},{"type":["string","null"]}]},"role":{"type":["string","null"]},"permissions":{"type":"array","items":{"$ref":"#/components/schemas/Permission"}},"limits":{"allOf":[{"$ref":"#/components/schemas/RoleLimits"},{"type":["object","null"]}]},"secondFactor":{"$ref":"#/components/schemas/AccessSecondFactor"}},"required":["scope","organizationId","venueId","memberId","role","permissions","limits","secondFactor"],"description":"What the signed-in person may do here: the effective permission set and the limits, from the function every server-side check uses (spec/07 §6.3)."},"AccessScope":{"type":"string","enum":["platform","organization","venue"]},"AccessSecondFactor":{"type":"object","properties":{"required":{"type":"boolean"},"satisfied":{"type":"boolean"}},"required":["required","satisfied"],"description":"Whether the console needs a second factor here, and whether this session has one."},"MyOrganizationList":{"type":"object","properties":{"data":{"type":"array","items":{"$ref":"#/components/schemas/MyOrganization"}}},"required":["data"]},"MyOrganization":{"type":"object","properties":{"id":{"$ref":"#/components/schemas/OrganizationId"},"name":{"type":"string"},"slug":{"type":"string"},"currency":{"$ref":"#/components/schemas/CurrencyCode"},"memberId":{"$ref":"#/components/schemas/MemberId"},"role":{"$ref":"#/components/schemas/RoleName"},"allVenues":{"type":"boolean"},"secondFactor":{"$ref":"#/components/schemas/AccessSecondFactor"}},"required":["id","name","slug","currency","memberId","role","allVenues","secondFactor"],"description":"An organisation the signed-in person is on the staff of."},"MyVenueList":{"type":"object","properties":{"data":{"type":"array","items":{"$ref":"#/components/schemas/MyVenue"}}},"required":["data"]},"MyVenue":{"type":"object","properties":{"id":{"$ref":"#/components/schemas/VenueId"},"organizationId":{"$ref":"#/components/schemas/OrganizationId"},"name":{"type":"string"},"slug":{"$ref":"#/components/schemas/VenueSlug"},"timezone":{"$ref":"#/components/schemas/Timezone"},"currency":{"$ref":"#/components/schemas/CurrencyCode"},"status":{"$ref":"#/components/schemas/VenueStatus"}},"required":["id","organizationId","name","slug","timezone","currency","status"],"description":"A venue the signed-in person works at."},"SetStaffPin":{"type":"object","properties":{"pin":{"$ref":"#/components/schemas/StaffPin"}},"required":["pin"],"additionalProperties":false},"StaffPin":{"type":"string","pattern":"^\\d{6}$","description":"Six digits. Never returned, never logged, never in an audit row."},"SharedDevice":{"type":"object","properties":{"organizationId":{"$ref":"#/components/schemas/OrganizationId"},"venueId":{"$ref":"#/components/schemas/VenueId"},"locked":{"type":"boolean"},"holder":{"allOf":[{"$ref":"#/components/schemas/SharedDeviceHolder"},{"type":["object","null"]}]},"idleLockMinutes":{"type":"integer","minimum":1},"staff":{"type":"array","items":{"$ref":"#/components/schemas/SharedDeviceStaff"}}},"required":["organizationId","venueId","locked","holder","idleLockMinutes","staff"],"description":"A shared counter device: its venue and who holds its PIN session."},"SharedDeviceStaff":{"type":"object","properties":{"memberId":{"$ref":"#/components/schemas/MemberId"},"name":{"type":["string","null"]},"pinLocked":{"type":"boolean"}},"required":["memberId","name","pinLocked"],"description":"Someone who can unlock this counter device: a PIN at its venue."},"UnlockSharedDevice":{"type":"object","properties":{"pin":{"$ref":"#/components/schemas/StaffPin"}},"required":["pin"],"additionalProperties":false},"ConfirmContactChange":{"type":"object","properties":{"code":{"$ref":"#/components/schemas/OtpCode"}},"required":["code"],"additionalProperties":false},"SignedInSessionList":{"type":"object","properties":{"data":{"type":"array","items":{"$ref":"#/components/schemas/SignedInSession"}}},"required":["data"],"description":"Your signed-in sessions that have not expired."},"SignedInSession":{"type":"object","properties":{"id":{"$ref":"#/components/schemas/AuthSessionId"},"current":{"type":"boolean"},"createdAt":{"$ref":"#/components/schemas/IsoDateTime"},"lastActiveAt":{"$ref":"#/components/schemas/IsoDateTime"},"expiresAt":{"$ref":"#/components/schemas/IsoDateTime"},"ipAddress":{"type":["string","null"]},"userAgent":{"type":["string","null"]},"device":{"type":["string","null"]},"loginMethod":{"type":["string","null"]},"secondFactor":{"type":"boolean"}},"required":["id","current","createdAt","lastActiveAt","expiresAt","ipAddress","userAgent","device","loginMethod","secondFactor"],"description":"One of your signed-in sessions: a sign-in on one device."},"AuthSessionId":{"type":"string","pattern":"^aus_[0-7][0123456789abcdefghjkmnpqrstvwxyz]{25}$","description":"A TypeID: \"aus_\" followed by the base32 form of a UUIDv7.","example":"aus_01k5znw4hmfay8vvr14d2pf2db"},"Meta":{"type":"object","properties":{"brand":{"$ref":"#/components/schemas/PublicBrand"},"flags":{"type":"object","additionalProperties":{"type":"boolean"}}},"required":["brand","flags"],"description":"What every client reads first: the brand configuration and the platform’s feature flags."},"PublicBrand":{"type":"object","properties":{"version":{"type":"integer","minimum":1,"maximum":1000},"revision":{"type":"string","pattern":"^[0-9a-f]{16}$"},"identity":{"$ref":"#/components/schemas/BrandIdentity"},"visuals":{"$ref":"#/components/schemas/PublicBrandVisuals"},"domains":{"$ref":"#/components/schemas/BrandDomains"},"apps":{"$ref":"#/components/schemas/BrandApps"},"legal":{"$ref":"#/components/schemas/BrandLegal"},"defaults":{"$ref":"#/components/schemas/BrandDefaults"}},"required":["version","revision","identity","visuals","domains","apps","defaults"],"description":"The brand configuration as clients read it (spec/03 §12)."},"BrandIdentity":{"type":"object","properties":{"productName":{"type":"string","minLength":1,"maxLength":40},"shortName":{"type":"string","minLength":1,"maxLength":20},"legalEntityName":{"type":"string","minLength":1,"maxLength":160},"registeredAddress":{"$ref":"#/components/schemas/Address"},"support":{"$ref":"#/components/schemas/BrandSupport"},"website":{"$ref":"#/components/schemas/HttpsUrl"}},"required":["productName","shortName","support","website"],"additionalProperties":false,"description":"Who the Operator is: product name, legal entity, support contacts, website."},"BrandSupport":{"type":"object","properties":{"email":{"type":"string","maxLength":254,"format":"email"},"phone":{"$ref":"#/components/schemas/PhoneNumber"}},"required":["email"],"additionalProperties":false,"description":"Where players and staff get help. Shown in every message the platform sends."},"HttpsUrl":{"type":"string","maxLength":2048,"format":"uri","description":"An absolute https URL.","example":"https://clavian.io/legal/terms"},"PublicBrandVisuals":{"type":"object","properties":{"logo":{"$ref":"#/components/schemas/BrandLogoUrls"},"appIcon":{"type":"string","format":"uri"},"splash":{"type":"string","format":"uri"},"favicon":{"type":"string","format":"uri"},"kioskScreensaver":{"$ref":"#/components/schemas/BrandKioskScreensaverUrls"},"theme":{"$ref":"#/components/schemas/BrandThemeValues"},"tokens":{"$ref":"#/components/schemas/BrandTokens"},"organizationBranding":{"$ref":"#/components/schemas/OrganizationBrandingLimits"}},"required":["logo","kioskScreensaver","theme","tokens","organizationBranding"],"description":"Logos, icons, default media, the theme and its computed tokens."},"BrandLogoUrls":{"type":"object","properties":{"light":{"type":"string","format":"uri"},"dark":{"type":"string","format":"uri"}},"description":"Where the logo for light and for dark grounds is served. Absent: the Clavian mark."},"BrandKioskScreensaverUrls":{"type":"object","properties":{"media":{"type":"array","items":{"type":"string","format":"uri"}}},"required":["media"],"description":"Where the kiosk’s default attract media is served."},"BrandThemeValues":{"type":"object","properties":{"colour":{"$ref":"#/components/schemas/BrandColourValues"},"surface":{"$ref":"#/components/schemas/BrandSurface"},"radius":{"$ref":"#/components/schemas/BrandRadius"},"font":{"$ref":"#/components/schemas/BrandFontValues"}},"required":["colour","surface","radius","font"],"description":"The brand’s design-token choices, with the defaults filled in where it made none."},"BrandColourValues":{"type":"object","properties":{"primary":{"$ref":"#/components/schemas/HexColour"},"accent":{"$ref":"#/components/schemas/HexColour"}},"description":"The brand’s colours. Absent: Clavian’s."},"HexColour":{"type":"string","pattern":"^#[0-9A-Fa-f]{6}$","description":"An opaque sRGB colour, `#RRGGBB`.","example":"#7C3AED"},"BrandSurface":{"type":"string","enum":["dark","light"],"description":"The ground a brand’s surfaces stand on: the dark or the light neutral set."},"BrandRadius":{"type":"string","enum":["sharp","soft","round"],"description":"Corner style, mapped onto the radius scale: `sharp`, `soft` (Clavian’s) or `round`."},"BrandFontValues":{"type":"object","properties":{"display":{"type":"string"},"body":{"type":"string"}},"description":"The brand’s font ids. Absent: Clavian’s."},"BrandTokens":{"type":"object","properties":{"shared":{"type":"object","additionalProperties":{"type":"string"}},"dark":{"type":"object","additionalProperties":{"type":"string"}},"light":{"type":"object","additionalProperties":{"type":"string"}}},"required":["shared","dark","light"],"description":"Design-token overrides by name: in every theme, in dark only, in light only."},"OrganizationBrandingLimits":{"type":"object","properties":{"colours":{"type":"boolean"},"logo":{"type":"boolean"}},"required":["colours","logo"],"additionalProperties":false,"description":"What an organisation may brand on its venues’ lock screens and receipts."},"BrandDomains":{"type":"object","properties":{"api":{"type":"string","minLength":1},"app":{"type":"string","minLength":1},"play":{"type":"string","minLength":1},"cdn":{"type":"string","minLength":1},"linkHosts":{"type":"array","items":{"type":"string","minLength":1}}},"required":["api","app","play","linkHosts"],"description":"The deployment’s hosts: the api, the console, player web, the CDN, and the app-link hosts."},"BrandApps":{"type":"object","properties":{"player":{"$ref":"#/components/schemas/BrandApp"},"staff":{"$ref":"#/components/schemas/BrandApp"}},"required":["player","staff"],"additionalProperties":false,"description":"The two store apps under the Operator’s brand (spec/15 §5)."},"BrandApp":{"type":"object","properties":{"iosBundleId":{"type":"string","maxLength":155,"pattern":"^[A-Za-z0-9-]+(?:\\.[A-Za-z0-9-]+)+$"},"androidApplicationId":{"type":"string","maxLength":150,"pattern":"^[a-zA-Z][a-zA-Z0-9_]*(?:\\.[a-zA-Z][a-zA-Z0-9_]*)+$"},"storeName":{"type":"string","minLength":1,"maxLength":30},"urlScheme":{"type":"string","maxLength":40,"pattern":"^[a-z][a-z0-9+.-]*$"}},"required":["iosBundleId","androidApplicationId","storeName","urlScheme"],"additionalProperties":false,"description":"One store app: its bundle and application ids, store name and URL scheme."},"BrandLegal":{"type":"object","properties":{"terms":{"$ref":"#/components/schemas/LegalDocument"},"privacy":{"$ref":"#/components/schemas/LegalDocument"},"refund":{"$ref":"#/components/schemas/RefundPolicyLink"},"copy":{"type":"object","additionalProperties":{"$ref":"#/components/schemas/BrandLegalCopy"}}},"required":["terms","privacy","refund"],"additionalProperties":false,"description":"The terms, privacy policy and refund policy in force, with the versions a sign-up must have shown, and any copy of the Operator’s own."},"LegalDocument":{"type":"object","properties":{"version":{"$ref":"#/components/schemas/LegalDocumentVersion"},"url":{"type":"string","format":"uri"}},"required":["version","url"],"description":"A legal document: the version in force and where to read it."},"RefundPolicyLink":{"type":"object","properties":{"url":{"type":"string","format":"uri"}},"required":["url"],"description":"Where the refund policy lives. It is shown, not consented to."},"BrandLegalCopy":{"type":"object","properties":{"cookieNotice":{"type":"string","minLength":1,"maxLength":600},"consentNotice":{"type":"string","minLength":1,"maxLength":1200}},"additionalProperties":false,"description":"The cookie notice and the consent wording, where the Operator’s must replace the catalogue’s."},"BrandDefaults":{"type":"object","properties":{"currency":{"$ref":"#/components/schemas/CurrencyCode"},"locale":{"$ref":"#/components/schemas/BrandLocale"},"timezone":{"$ref":"#/components/schemas/Timezone"},"country":{"$ref":"#/components/schemas/CountryCode"}},"required":["currency","locale","timezone","country"],"additionalProperties":false,"description":"What a new organisation starts with: currency, locale, timezone, country."},"BrandLocale":{"type":"string","pattern":"^[a-z]{2,3}(?:-[A-Z]{2})?$","description":"A BCP 47 language tag: `en`, `hi`, `en-IN`.","example":"en-IN"},"HqBrandView":{"type":"object","properties":{"revision":{"type":"integer","minimum":0,"maximum":2147483647},"updatedAt":{"$ref":"#/components/schemas/IsoDateTime"},"values":{"$ref":"#/components/schemas/BrandRuntimeValues"},"deployment":{"$ref":"#/components/schemas/BrandDeploymentValues"},"messaging":{"$ref":"#/components/schemas/BrandMessaging"},"brand":{"$ref":"#/components/schemas/PublicBrand"}},"required":["revision","values","deployment","messaging","brand"],"description":"The brand as HQ edits it: the values HQ has set, the deployment’s, and the brand clients get."},"BrandRuntimeValues":{"type":"object","properties":{"support":{"$ref":"#/components/schemas/BrandSupport"},"legal":{"$ref":"#/components/schemas/BrandLegal"},"theme":{"$ref":"#/components/schemas/BrandTheme"},"kioskScreensaver":{"$ref":"#/components/schemas/BrandKioskScreensaver"},"organizationBranding":{"$ref":"#/components/schemas/OrganizationBrandingLimits"}},"additionalProperties":false,"description":"The brand values HQ has set. A group left out is the deployment’s."},"BrandTheme":{"type":"object","properties":{"colour":{"$ref":"#/components/schemas/BrandColours"},"surface":{"$ref":"#/components/schemas/BrandSurface"},"radius":{"$ref":"#/components/schemas/BrandRadius"},"font":{"$ref":"#/components/schemas/BrandFonts"}},"additionalProperties":false,"description":"A brand’s design tokens: primary and accent colours, a dark or light surface, a corner style and two fonts from the list. What is left out is the Clavian default. A colour below WCAG AA on the surface is refused."},"BrandColours":{"type":"object","properties":{"primary":{"$ref":"#/components/schemas/HexColour"},"accent":{"$ref":"#/components/schemas/HexColour"}},"additionalProperties":false,"description":"The primary (fills) and accent (text and links) colours. Either may be left to the default."},"BrandFonts":{"type":"object","properties":{"display":{"type":"string","description":"A display font from the brand list (spec/03 §12): big-shoulders-display, archivo, barlow-condensed, oswald, chakra-petch, exo-2, rajdhani."},"body":{"type":"string","description":"A body font from the brand list (spec/03 §12): instrument-sans, archivo, hanken-grotesk, exo-2, mukta, ibm-plex-sans, source-sans-3."}},"additionalProperties":false,"description":"The heading and body faces, by id from the brand list."},"BrandKioskScreensaver":{"type":"object","properties":{"media":{"type":"array","items":{"$ref":"#/components/schemas/BrandMediaPath"},"maxItems":20}},"required":["media"],"additionalProperties":false,"description":"The kiosk’s default attract media, for a venue that has uploaded none. Empty: the brand’s animated background."},"BrandMediaPath":{"type":"string","maxLength":200,"pattern":"^brand\\/(?:[A-Za-z0-9_-][A-Za-z0-9._-]*\\/)*[A-Za-z0-9_-][A-Za-z0-9._-]*\\.(?:png|webp|jpe?g|mp4|webm)$","description":"An image or video under `brand/` in public-media.","example":"brand/attract/1.webp"},"BrandDeploymentValues":{"type":"object","properties":{"support":{"$ref":"#/components/schemas/BrandSupport"},"legal":{"$ref":"#/components/schemas/BrandLegal"},"theme":{"$ref":"#/components/schemas/BrandTheme"},"kioskScreensaver":{"$ref":"#/components/schemas/BrandKioskScreensaver"},"organizationBranding":{"$ref":"#/components/schemas/OrganizationBrandingLimits"}},"required":["support","theme","kioskScreensaver","organizationBranding"],"additionalProperties":false,"description":"The deployment’s value of each group HQ may edit."},"BrandMessaging":{"type":"object","properties":{"email":{"$ref":"#/components/schemas/BrandEmailSender"},"sms":{"$ref":"#/components/schemas/BrandSmsSender"},"whatsapp":{"$ref":"#/components/schemas/BrandWhatsApp"}},"required":["email"],"additionalProperties":false,"description":"Who messages come from: the email sender, the SMS sender id, the WhatsApp number."},"BrandEmailSender":{"type":"object","properties":{"name":{"type":"string","pattern":"^[^\"\\\\<>\\r\\n]{1,60}$"},"address":{"type":"string","maxLength":254,"format":"email"}},"required":["name","address"],"additionalProperties":false,"description":"The From of every email the platform sends."},"BrandSmsSender":{"type":"object","properties":{"senderId":{"type":"string","pattern":"^[A-Za-z0-9]{3,11}$"},"dltEntityId":{"type":"string","pattern":"^\\d{1,30}$"}},"required":["senderId"],"additionalProperties":false,"description":"The SMS sender id and its DLT registration."},"BrandWhatsApp":{"type":"object","properties":{"businessNumber":{"$ref":"#/components/schemas/PhoneNumber"}},"required":["businessNumber"],"additionalProperties":false,"description":"The WhatsApp business number messages come from."},"BrandRuntimeChange":{"type":"object","properties":{"support":{"$ref":"#/components/schemas/BrandSupport"},"legal":{"$ref":"#/components/schemas/BrandLegal"},"theme":{"$ref":"#/components/schemas/BrandTheme"},"kioskScreensaver":{"$ref":"#/components/schemas/BrandKioskScreensaver"},"organizationBranding":{"$ref":"#/components/schemas/OrganizationBrandingLimits"},"reset":{"type":"array","items":{"$ref":"#/components/schemas/BrandRuntimeGroup"},"maxItems":5}},"additionalProperties":false,"description":"Set groups to new values, or reset them to the deployment’s; groups in neither are unchanged."},"BrandRuntimeGroup":{"type":"string","enum":["support","legal","theme","kioskScreensaver","organizationBranding"],"description":"A group of the brand configuration HQ edits at run time."},"RealtimeToken":{"type":"object","properties":{"token":{"type":"string","minLength":1},"kind":{"$ref":"#/components/schemas/RealtimePrincipalKind"},"principalId":{"type":"string","minLength":1},"expiresAt":{"$ref":"#/components/schemas/IsoDateTime"}},"required":["token","kind","principalId","expiresAt"],"description":"A realtime token: five minutes, for the first frame on the socket."},"RealtimePrincipalKind":{"type":"string","enum":["station","member","player"],"description":"Who holds a realtime socket: a station, a member (staff), or a player."},"RealtimeTokenRequest":{"type":"object","properties":{"organizationId":{"$ref":"#/components/schemas/OrganizationId"}},"description":"Which principal the token is for: a member of this organisation, or (with nothing) the player."},"TwoFactorEnrolment":{"type":"object","properties":{"method":{"type":"string","enum":["totp"]},"totpURI":{"type":"string"},"backupCodes":{"type":"array","items":{"type":"string"}}},"required":["method","totpURI","backupCodes"],"description":"An authenticator to set up. It is not active until its first code is verified."},"AuthError":{"type":"object","properties":{"code":{"type":"string"},"message":{"type":"string"}},"required":["code","message"],"description":"A refusal from the authentication endpoints (better-auth’s shape, not problem+json)."},"TwoFactorEnableRequest":{"type":"object","properties":{"password":{"type":"string","minLength":1,"maxLength":256},"issuer":{"type":"string","minLength":1,"maxLength":64}}},"TwoFactorVerified":{"type":"object","properties":{"token":{"type":"string"},"user":{"$ref":"#/components/schemas/TwoFactorUser"}},"required":["token","user"],"description":"The second factor is proven; the session records it."},"TwoFactorUser":{"type":"object","properties":{"id":{"type":"string"},"twoFactorEnabled":{"type":"boolean"}},"required":["id"]},"TwoFactorCodeRequest":{"type":"object","properties":{"code":{"type":"string","minLength":1,"maxLength":64}},"required":["code"],"description":"A six-digit authenticator code, or a backup code."},"TwoFactorBackupCodes":{"type":"object","properties":{"status":{"type":"boolean"},"backupCodes":{"type":"array","items":{"type":"string"}}},"required":["status","backupCodes"],"description":"A new set of backup codes; the old set no longer works."},"TwoFactorPasswordRequest":{"type":"object","properties":{"password":{"type":"string","minLength":1,"maxLength":256}}},"TwoFactorStatus":{"type":"object","properties":{"status":{"type":"boolean"}},"required":["status"]},"AuthMessage":{"type":"object","properties":{"message":{"type":"string"}},"required":["message"]},"PhoneCodeRequest":{"type":"object","properties":{"phoneNumber":{"$ref":"#/components/schemas/PhoneNumber"}},"required":["phoneNumber"]},"PhoneVerified":{"type":"object","properties":{"status":{"type":"boolean"},"token":{"type":["string","null"]},"user":{"$ref":"#/components/schemas/AuthUser"}},"required":["status","token","user"],"description":"Signed in — with the account created on the first verification of a new number."},"AuthUser":{"type":"object","properties":{"id":{"type":"string"},"name":{"type":"string"},"email":{"type":"string"},"emailVerified":{"type":"boolean"},"image":{"type":["string","null"]},"phoneNumber":{"type":["string","null"]},"phoneNumberVerified":{"type":["boolean","null"]},"username":{"type":["string","null"]},"displayUsername":{"type":["string","null"]},"twoFactorEnabled":{"type":["boolean","null"]},"createdAt":{"$ref":"#/components/schemas/IsoDateTime"},"updatedAt":{"$ref":"#/components/schemas/IsoDateTime"}},"required":["id","name","email","emailVerified","createdAt","updatedAt"],"description":"The signed-in person, as better-auth returns them."},"VerifyPhoneNumberRequest":{"type":"object","properties":{"phoneNumber":{"$ref":"#/components/schemas/PhoneNumber"},"code":{"$ref":"#/components/schemas/OtpCode"},"disableSession":{"type":"boolean"},"consent":{"$ref":"#/components/schemas/SignUpConsent"}},"required":["phoneNumber","code"],"description":"Nothing else is accepted: an unexpected field is refused."},"AuthStatus":{"type":"object","properties":{"status":{"type":"boolean"}},"required":["status"]},"ResetPasswordWithPhoneCodeRequest":{"type":"object","properties":{"phoneNumber":{"$ref":"#/components/schemas/PhoneNumber"},"otp":{"$ref":"#/components/schemas/OtpCode"},"newPassword":{"type":"string","minLength":8,"maxLength":128}},"required":["phoneNumber","otp","newPassword"]},"SignedUp":{"type":"object","properties":{"token":{"type":["string","null"]},"user":{"$ref":"#/components/schemas/AuthUser"}},"required":["token","user"],"description":"Accepted. The same answer for an address that already has an account, so the answer says nothing about which addresses exist."},"AuthErrorWithSuggestions":{"type":"object","properties":{"code":{"type":"string"},"message":{"type":"string"},"suggestions":{"type":"array","items":{"$ref":"#/components/schemas/Username"}}},"required":["code","message"],"description":"A refusal from the authentication endpoints; a taken gamer tag carries alternatives."},"Username":{"type":"string","minLength":3,"maxLength":20,"pattern":"^[A-Za-z0-9_.]+$","description":"A gamer tag: 3–20 letters, digits, \"_\" or \".\". Unique, ignoring case.","example":"rohan_i"},"SignUpWithEmailRequest":{"type":"object","properties":{"name":{"type":"string"},"email":{"type":"string","format":"email"},"password":{"type":"string","minLength":8,"maxLength":128},"username":{"$ref":"#/components/schemas/Username"},"consent":{"$ref":"#/components/schemas/SignUpConsent"},"rememberMe":{"type":"boolean"}},"required":["name","email","password","consent"]},"AuthSuccess":{"type":"object","properties":{"success":{"type":"boolean"}},"required":["success"]},"SendEmailCodeRequest":{"type":"object","properties":{"email":{"type":"string","format":"email"},"type":{"type":"string","enum":["email-verification","sign-in","forget-password"]}},"required":["email","type"]},"EmailVerified":{"type":"object","properties":{"status":{"type":"boolean"},"token":{"type":["string","null"]},"user":{"$ref":"#/components/schemas/AuthUser"}},"required":["status","token","user"]},"EmailCodeRequest":{"type":"object","properties":{"email":{"type":"string","format":"email"},"otp":{"$ref":"#/components/schemas/OtpCode"}},"required":["email","otp"],"description":"An address and the code emailed to it."},"PasswordSignIn":{"type":"object","properties":{"token":{"type":"string"},"user":{"$ref":"#/components/schemas/AuthUser"},"redirect":{"type":"boolean"},"url":{"type":["string","null"]},"twoFactorRedirect":{"type":"boolean"},"twoFactorMethods":{"type":"array","items":{"type":"string"}}},"description":"Either a session (`token` and `user`), or — for an account with a second factor — `twoFactorRedirect`."},"SignInWithEmailRequest":{"type":"object","properties":{"email":{"type":"string","format":"email"},"password":{"type":"string","minLength":1,"maxLength":128},"rememberMe":{"type":"boolean"}},"required":["email","password"]},"SignInWithUsernameRequest":{"type":"object","properties":{"username":{"type":"string","minLength":1,"maxLength":64},"password":{"type":"string","minLength":1,"maxLength":128},"rememberMe":{"type":"boolean"}},"required":["username","password"]},"SignedIn":{"type":"object","properties":{"token":{"type":"string"},"user":{"$ref":"#/components/schemas/AuthUser"}},"required":["token","user"]},"EmailPasswordResetRequest":{"type":"object","properties":{"email":{"type":"string","format":"email"}},"required":["email"]},"ResetPasswordWithEmailCodeRequest":{"type":"object","properties":{"email":{"type":"string","format":"email"},"otp":{"$ref":"#/components/schemas/OtpCode"},"password":{"type":"string","minLength":8,"maxLength":128}},"required":["email","otp","password"]},"UsernameAvailability":{"type":"object","properties":{"available":{"type":"boolean"},"suggestions":{"type":"array","items":{"$ref":"#/components/schemas/Username"}}},"required":["available"]},"UsernameAvailabilityRequest":{"type":"object","properties":{"username":{"type":"string","minLength":1,"maxLength":64}},"required":["username"]},"UpdateUserRequest":{"type":"object","properties":{"username":{"$ref":"#/components/schemas/Username"}},"required":["username"]},"SocialSignIn":{"type":"object","properties":{"redirect":{"type":"boolean"},"url":{"type":"string"},"token":{"type":"string"},"user":{"$ref":"#/components/schemas/AuthUser"}},"required":["redirect"],"description":"The provider's URL to send the player to — or, with a Google ID token, a session."},"SocialSignInRequest":{"type":"object","properties":{"provider":{"$ref":"#/components/schemas/SocialSignInProvider"},"callbackURL":{"type":"string","description":"Where the browser lands, signed in: a page of an origin the api trusts (the player web), or a path."},"errorCallbackURL":{"type":"string","description":"Where the browser lands when the sign-in is refused, with `?error=<code>`: `CONSENT_REQUIRED` for a new player who sent no `consent` (show the consents and start again with them), `account_not_linked` for an address that already has an account (sign in, then link), `BANNED_USER`, `state_mismatch`, `access_denied`, `invalid_assertion` (Steam), `account_already_linked_to_different_user` (a link)."},"newUserCallbackURL":{"type":"string","description":"Where a sign-in that created the account lands, when that differs from `callbackURL`."},"disableRedirect":{"type":"boolean"},"idToken":{"$ref":"#/components/schemas/GoogleIdToken"},"consent":{"allOf":[{"$ref":"#/components/schemas/SignUpConsent"},{"description":"Terms, privacy and marketing (F-CMP-06), for a player who may be new: kept with the flow — never in a URL — and written with the account if the provider round trip creates one. Ignored for a player who has an account. Without it, a round trip that would create an account is refused with `CONSENT_REQUIRED`."}]}},"required":["provider"],"description":"Nothing else is accepted: an unexpected field is refused."},"SocialSignInProvider":{"type":"string","enum":["google","discord"],"description":"An OAuth provider a player may sign in with."},"GoogleIdToken":{"type":"object","properties":{"token":{"type":"string","minLength":1},"nonce":{"type":"string"}},"required":["token"],"description":"A Google ID token from the native sign-in SDK in the phone apps."},"SteamStart":{"type":"object","properties":{"url":{"type":"string"}},"required":["url"]},"SteamStartRequest":{"type":"object","properties":{"callbackURL":{"type":"string","description":"Where the browser lands, signed in: a page of an origin the api trusts (the player web), or a path."},"errorCallbackURL":{"type":"string","description":"Where the browser lands when the sign-in is refused, with `?error=<code>`: `CONSENT_REQUIRED` for a new player who sent no `consent` (show the consents and start again with them), `account_not_linked` for an address that already has an account (sign in, then link), `BANNED_USER`, `state_mismatch`, `access_denied`, `invalid_assertion` (Steam), `account_already_linked_to_different_user` (a link)."},"newUserCallbackURL":{"type":"string","description":"Where a sign-in that created the account lands, when that differs from `callbackURL`."},"consent":{"allOf":[{"$ref":"#/components/schemas/SignUpConsent"},{"description":"Terms, privacy and marketing (F-CMP-06), for a player who may be new: kept with the flow — never in a URL — and written with the account if the provider round trip creates one. Ignored for a player who has an account. Without it, a round trip that would create an account is refused with `CONSENT_REQUIRED`."}]},"link":{"type":"boolean"}},"description":"Nothing else is accepted: an unexpected field is refused."},"LinkSocialAccount":{"type":"object","properties":{"url":{"type":"string"},"redirect":{"type":"boolean"}},"required":["url","redirect"]},"LinkSocialAccountRequest":{"type":"object","properties":{"provider":{"$ref":"#/components/schemas/SocialSignInProvider"},"callbackURL":{"type":"string","description":"Where the browser lands, signed in: a page of an origin the api trusts (the player web), or a path."},"errorCallbackURL":{"type":"string","description":"Where the browser lands when the sign-in is refused, with `?error=<code>`: `CONSENT_REQUIRED` for a new player who sent no `consent` (show the consents and start again with them), `account_not_linked` for an address that already has an account (sign in, then link), `BANNED_USER`, `state_mismatch`, `access_denied`, `invalid_assertion` (Steam), `account_already_linked_to_different_user` (a link)."},"disableRedirect":{"type":"boolean"}},"required":["provider"],"description":"Nothing else is accepted: an unexpected field is refused."},"LinkedAccount":{"type":"object","properties":{"id":{"type":"string"},"providerId":{"type":"string"},"accountId":{"type":"string"},"userId":{"type":"string"},"scopes":{"type":"array","items":{"type":"string"}},"createdAt":{"$ref":"#/components/schemas/IsoDateTime"},"updatedAt":{"$ref":"#/components/schemas/IsoDateTime"}},"required":["id","providerId","accountId","userId","scopes","createdAt","updatedAt"],"description":"A way into the account: its password, or a provider linked to it."},"AuthSessionState":{"type":["object","null"],"properties":{"session":{"$ref":"#/components/schemas/AuthSession"},"user":{"$ref":"#/components/schemas/AuthUser"}},"required":["session","user"],"description":"The caller’s session and who it belongs to, or `null` when not signed in."},"AuthSession":{"type":"object","properties":{"id":{"type":"string"},"token":{"type":"string"},"userId":{"type":"string"},"expiresAt":{"$ref":"#/components/schemas/IsoDateTime"},"createdAt":{"$ref":"#/components/schemas/IsoDateTime"},"updatedAt":{"$ref":"#/components/schemas/IsoDateTime"},"ipAddress":{"type":["string","null"]},"userAgent":{"type":["string","null"]},"activeOrganizationId":{"type":["string","null"]},"impersonatedBy":{"type":["string","null"]},"secondFactor":{"type":["string","null"]},"secondFactorAt":{"allOf":[{"$ref":"#/components/schemas/IsoDateTime"},{"type":["string","null"]}]}},"required":["id","token","userId","expiresAt","createdAt","updatedAt"],"description":"A better-auth session."},"OrganizationPlayerList":{"type":"object","properties":{"data":{"type":"array","items":{"$ref":"#/components/schemas/OrganizationPlayer"}},"page":{"$ref":"#/components/schemas/Page"}},"required":["data","page"]},"OrganizationPlayer":{"type":"object","properties":{"id":{"$ref":"#/components/schemas/PlayerId"},"username":{"type":["string","null"]},"displayName":{"type":["string","null"]},"firstName":{"type":["string","null"]},"lastName":{"type":["string","null"]},"phoneNumber":{"type":["string","null"]},"email":{"type":["string","null"]},"contactMasked":{"type":"boolean"},"tags":{"type":"array","items":{"type":"string"}},"knownSince":{"$ref":"#/components/schemas/IsoDateTime"}},"required":["id","username","displayName","firstName","lastName","phoneNumber","email","contactMasked","tags","knownSince"],"description":"A player as one organisation sees them: profile basics and its own tags (spec/13 §8)."},"PlayerTag":{"type":"string","minLength":1,"maxLength":32,"pattern":"^[A-Za-z0-9](?:[A-Za-z0-9 _-]{0,30}[A-Za-z0-9])?$","description":"Only players with this tag: a list, or an audience.","example":"regulars"},"OrganizationPlayerProfile":{"type":"object","properties":{"id":{"$ref":"#/components/schemas/PlayerId"},"username":{"type":["string","null"]},"displayName":{"type":["string","null"]},"firstName":{"type":["string","null"]},"lastName":{"type":["string","null"]},"phoneNumber":{"type":["string","null"]},"email":{"type":["string","null"]},"contactMasked":{"type":"boolean"},"tags":{"type":"array","items":{"type":"string"}},"knownSince":{"$ref":"#/components/schemas/IsoDateTime"},"notes":{"type":"array","items":{"$ref":"#/components/schemas/PlayerNote"}}},"required":["id","username","displayName","firstName","lastName","phoneNumber","email","contactMasked","tags","knownSince","notes"],"description":"One player as one organisation sees them: profile basics, its tags and its notes (spec/13 §8). The fields that belong to features not built yet are added by those features."},"PlayerNote":{"type":"object","properties":{"id":{"$ref":"#/components/schemas/PlayerNoteId"},"body":{"type":"string"},"author":{"$ref":"#/components/schemas/PlayerNoteAuthor"},"createdAt":{"$ref":"#/components/schemas/IsoDateTime"},"updatedAt":{"$ref":"#/components/schemas/IsoDateTime"}},"required":["id","body","author","createdAt","updatedAt"],"description":"A staff note about a player. This organisation’s own, and never shown to the player (F-ORG-06)."},"PlayerNoteId":{"type":"string","pattern":"^pnt_[0-7][0123456789abcdefghjkmnpqrstvwxyz]{25}$","description":"A TypeID: \"pnt_\" followed by the base32 form of a UUIDv7.","example":"pnt_01k5znw4hmfay8vvr14d2pf2db"},"PlayerNoteAuthor":{"type":"object","properties":{"name":{"type":["string","null"]}},"required":["name"],"description":"Who wrote a note."},"UpdateOrganizationPlayer":{"type":"object","properties":{"notes":{"$ref":"#/components/schemas/PlayerNoteChanges"},"tags":{"$ref":"#/components/schemas/PlayerTagChanges"}},"additionalProperties":false,"description":"Notes and tags — the only things an organisation writes on a player here (F-ORG-06)."},"PlayerNoteChanges":{"type":"object","properties":{"add":{"type":"array","items":{"$ref":"#/components/schemas/NewPlayerNote"},"maxItems":20},"edit":{"type":"array","items":{"$ref":"#/components/schemas/PlayerNoteEdit"},"maxItems":20},"remove":{"type":"array","items":{"$ref":"#/components/schemas/PlayerNoteId"},"maxItems":20}},"additionalProperties":false,"description":"Notes to add, to edit and to remove. Each one is audited."},"NewPlayerNote":{"type":"object","properties":{"body":{"type":"string","minLength":1,"maxLength":2000}},"required":["body"],"additionalProperties":false},"PlayerNoteEdit":{"type":"object","properties":{"id":{"$ref":"#/components/schemas/PlayerNoteId"},"body":{"type":"string","minLength":1,"maxLength":2000}},"required":["id","body"],"additionalProperties":false},"PlayerTagChanges":{"type":"object","properties":{"add":{"type":"array","items":{"allOf":[{"$ref":"#/components/schemas/PlayerTag"},{"description":"A label this organisation groups players by: `regulars`, `pays cash`. Stored lower-case."}]},"maxItems":20},"remove":{"type":"array","items":{"allOf":[{"$ref":"#/components/schemas/PlayerTag"},{"description":"A label this organisation groups players by: `regulars`, `pays cash`. Stored lower-case."}]},"maxItems":20}},"additionalProperties":false,"description":"Tags to add and to remove. Adding one the player has, or removing one they have not, changes nothing."},"PlayerSessionList":{"type":"object","properties":{"data":{"type":"array","items":{"$ref":"#/components/schemas/PlayerSessionEntry"}},"page":{"$ref":"#/components/schemas/Page"}},"required":["data","page"]},"PlayerSessionEntry":{"type":"object","properties":{"id":{"$ref":"#/components/schemas/SessionId"},"venueId":{"$ref":"#/components/schemas/VenueId"},"startedAt":{"$ref":"#/components/schemas/IsoDateTime"},"endedAt":{"allOf":[{"$ref":"#/components/schemas/IsoDateTime"},{"type":["string","null"]}]}},"required":["id","venueId","startedAt","endedAt"],"description":"A session the player played at this organisation. Filled by F-SES (M3)."},"SessionId":{"type":"string","pattern":"^ses_[0-7][0123456789abcdefghjkmnpqrstvwxyz]{25}$","description":"A TypeID: \"ses_\" followed by the base32 form of a UUIDv7.","example":"ses_01k5znw4hmfay8vvr14d2pf2db"},"PlayerOrderList":{"type":"object","properties":{"data":{"type":"array","items":{"$ref":"#/components/schemas/PlayerOrderEntry"}},"page":{"$ref":"#/components/schemas/Page"}},"required":["data","page"]},"PlayerOrderEntry":{"type":"object","properties":{"id":{"type":"string"},"venueId":{"$ref":"#/components/schemas/VenueId"},"placedAt":{"$ref":"#/components/schemas/IsoDateTime"},"total":{"$ref":"#/components/schemas/Money"}},"required":["id","venueId","placedAt","total"],"description":"An order the player placed at this organisation. Filled by F-POS (M3)."},"PlayerPassList":{"type":"object","properties":{"data":{"type":"array","items":{"$ref":"#/components/schemas/PlayerPassEntry"}},"page":{"$ref":"#/components/schemas/Page"}},"required":["data","page"]},"PlayerPassEntry":{"type":"object","properties":{"id":{"type":"string"},"name":{"type":"string"},"grantedSeconds":{"type":"integer","minimum":0},"remainingSeconds":{"type":"integer","minimum":0},"expiresAt":{"allOf":[{"$ref":"#/components/schemas/IsoDateTime"},{"type":["string","null"]}]}},"required":["id","name","grantedSeconds","remainingSeconds","expiresAt"],"description":"A pass the player holds at this organisation. Filled by F-PRC (M3)."},"PlayerWalletList":{"type":"object","properties":{"data":{"type":"array","items":{"$ref":"#/components/schemas/PlayerWalletEntry"}},"page":{"$ref":"#/components/schemas/Page"}},"required":["data","page"]},"PlayerWalletEntry":{"type":"object","properties":{"id":{"type":"string"},"occurredAt":{"$ref":"#/components/schemas/IsoDateTime"},"amount":{"$ref":"#/components/schemas/Money"},"description":{"type":"string"}},"required":["id","occurredAt","amount","description"],"description":"A line of the player’s wallet statement here. Filled by F-WAL (M3)."},"PlayerPointsList":{"type":"object","properties":{"data":{"type":"array","items":{"$ref":"#/components/schemas/PlayerPointsEntry"}},"page":{"$ref":"#/components/schemas/Page"}},"required":["data","page"]},"PlayerPointsEntry":{"type":"object","properties":{"id":{"type":"string"},"occurredAt":{"$ref":"#/components/schemas/IsoDateTime"},"venueId":{"allOf":[{"$ref":"#/components/schemas/VenueId"},{"type":["string","null"]}]},"points":{"type":"integer"}},"required":["id","occurredAt","venueId","points"],"description":"Points the player earned at this organisation. Filled by F-LOY (M3)."},"HqOrganizationList":{"type":"object","properties":{"data":{"type":"array","items":{"$ref":"#/components/schemas/HqOrganization"}},"page":{"$ref":"#/components/schemas/Page"}},"required":["data","page"]},"HqOrganization":{"type":"object","properties":{"id":{"$ref":"#/components/schemas/OrganizationId"},"name":{"type":"string"},"slug":{"type":"string"},"legalName":{"type":["string","null"]},"kind":{"$ref":"#/components/schemas/OrganizationKind"},"pan":{"type":["string","null"]},"registeredAddress":{"allOf":[{"$ref":"#/components/schemas/Address"},{"type":["object","null"]}]},"hqAccess":{"$ref":"#/components/schemas/HqAccess"},"status":{"$ref":"#/components/schemas/OrganizationStatus"},"currency":{"$ref":"#/components/schemas/CurrencyCode"},"createdAt":{"$ref":"#/components/schemas/IsoDateTime"}},"required":["id","name","slug","legalName","kind","pan","registeredAddress","hqAccess","status","currency","createdAt"],"description":"An organisation’s record, as HQ holds it (spec/14 §3.1, spec/04 §2)."},"OrganizationKind":{"type":"string","enum":["operator_owned","franchise","independent"],"description":"`operator_owned`: the Operator’s own. `franchise` and `independent`: not owned, reached as `hqAccess` allows."},"HqAccess":{"type":"string","enum":["aggregate_only","full"],"description":"`aggregate_only`: HQ sees aggregates and no row-level customer data. `full`: row-level too, each read audited."},"OrganizationStatus":{"type":"string","enum":["active","suspended"]},"CreatedHqOrganization":{"type":"object","properties":{"organization":{"$ref":"#/components/schemas/HqOrganization"},"ownerInvitation":{"$ref":"#/components/schemas/CreatedInvitation"}},"required":["organization","ownerInvitation"],"description":"The organisation just created, and its owner’s invitation."},"CreateHqOrganization":{"type":"object","properties":{"name":{"type":"string","minLength":1,"maxLength":120},"slug":{"$ref":"#/components/schemas/OrganizationSlug"},"legalName":{"type":"string","minLength":1,"maxLength":200},"kind":{"$ref":"#/components/schemas/OrganizationKind"},"pan":{"$ref":"#/components/schemas/Pan"},"registeredAddress":{"$ref":"#/components/schemas/Address"},"hqAccess":{"$ref":"#/components/schemas/HqAccess"},"owner":{"$ref":"#/components/schemas/OwnerInvitationContact"}},"required":["name","slug","legalName","kind","registeredAddress","owner"],"additionalProperties":false,"description":"A new organisation. Its currency is the deployment’s default (the brand configuration). Plan, subscription and network membership come with their features."},"OrganizationSlug":{"type":"string","minLength":3,"maxLength":60,"pattern":"^[a-z0-9]+(?:-[a-z0-9]+)*$","example":"playground-gaming"},"Pan":{"type":"string","pattern":"^[A-Z]{5}[0-9]{4}[A-Z]$","example":"AAACP1234Q"},"OwnerInvitationContact":{"type":"object","properties":{"phoneNumber":{"$ref":"#/components/schemas/PhoneNumber"},"email":{"type":"string","minLength":3,"maxLength":254,"pattern":"^[^\\s@]+@[^\\s@]+$"}},"additionalProperties":false,"description":"The person to invite as owner, by phone or by email — exactly one."},"LivenessResponse":{"type":"object","properties":{"status":{"type":"string","enum":["ok"]},"entrypoint":{"$ref":"#/components/schemas/ServerEntrypoint"},"uptimeSeconds":{"$ref":"#/components/schemas/DurationSeconds"}},"required":["status","entrypoint","uptimeSeconds"],"description":"The process is alive. No dependency was consulted."},"ServerEntrypoint":{"type":"string","enum":["api","realtime","worker","all"],"description":"Which entrypoint of the one image this process is running.","example":"api"},"DurationSeconds":{"type":"integer","minimum":0,"description":"A duration in whole seconds.","example":3600},"ReadinessResponse":{"type":"object","properties":{"status":{"type":"string","enum":["ok","degraded"]},"entrypoint":{"$ref":"#/components/schemas/ServerEntrypoint"},"checks":{"type":"array","items":{"$ref":"#/components/schemas/ReadinessCheck"}}},"required":["status","entrypoint","checks"],"description":"Whether this process should be sent traffic, and what each dependency said."},"ReadinessCheck":{"type":"object","properties":{"name":{"type":"string","minLength":1,"example":"database"},"status":{"type":"string","enum":["ok","failed","skipped"]},"detail":{"type":"string","maxLength":200},"tookMs":{"type":"integer","minimum":0}},"required":["name","status","tookMs"]},"StationCommandId":{"type":"string","pattern":"^cmd_[0-7][0123456789abcdefghjkmnpqrstvwxyz]{25}$","description":"A TypeID: \"cmd_\" followed by the base32 form of a UUIDv7.","example":"cmd_01k5znw4hmfay8vvr14d2pf2db"}},"parameters":{}},"paths":{"/v1/hello":{"get":{"operationId":"getHello","tags":["meta"],"summary":"Hello world","description":"The contract smoke endpoint. Returns a one-item collection so that Money, Page and Problem are exercised end to end.","parameters":[{"schema":{"type":"string","minLength":1,"description":"The `page.cursor` from the previous response."},"required":false,"description":"The `page.cursor` from the previous response.","name":"cursor","in":"query"},{"schema":{"type":"integer","minimum":1,"maximum":100,"default":25,"description":"Items per page, 1–100."},"required":false,"description":"Items per page, 1–100.","name":"limit","in":"query"},{"schema":{"type":"string","minLength":1,"maxLength":60,"description":"Who to greet.","example":"Clavian"},"required":false,"description":"Who to greet.","name":"name","in":"query"}],"responses":{"200":{"description":"A page of greetings.","content":{"application/json":{"schema":{"$ref":"#/components/schemas/HelloResponse"}}}},"400":{"description":"The query could not be understood. Codes: validation_failed.","content":{"application/problem+json":{"schema":{"$ref":"#/components/schemas/Problem"}}}},"429":{"description":"Too many requests. Codes: rate_limited.","content":{"application/problem+json":{"schema":{"$ref":"#/components/schemas/Problem"}}}},"500":{"description":"Something went wrong. Codes: internal_error.","content":{"application/problem+json":{"schema":{"$ref":"#/components/schemas/Problem"}}}}}}},"/v1/organizations/{organizationId}/scope-probes":{"get":{"operationId":"listScopeProbes","tags":["meta"],"summary":"List the organisation scoping smoke rows","description":"Returns only rows of the organisation in the path. The predicate is applied in code by the scoped repository and again by row-level security (spec/03 §7.1).","parameters":[{"schema":{"$ref":"#/components/schemas/OrganizationId"},"required":true,"description":"A TypeID: \"org_\" followed by the base32 form of a UUIDv7.","name":"organizationId","in":"path"},{"schema":{"type":"string","minLength":1,"description":"The `page.cursor` from the previous response."},"required":false,"description":"The `page.cursor` from the previous response.","name":"cursor","in":"query"},{"schema":{"type":"integer","minimum":1,"maximum":100,"default":25,"description":"Items per page, 1–100."},"required":false,"description":"Items per page, 1–100.","name":"limit","in":"query"}],"responses":{"200":{"description":"A page of rows, all of them this organisation’s.","content":{"application/json":{"schema":{"$ref":"#/components/schemas/ScopeProbeList"}}}},"400":{"description":"The request could not be understood. Codes: validation_failed.","content":{"application/problem+json":{"schema":{"$ref":"#/components/schemas/Problem"}}}},"401":{"description":"Not signed in. Codes: unauthenticated.","content":{"application/problem+json":{"schema":{"$ref":"#/components/schemas/Problem"}}}},"404":{"description":"No such organisation, or it is not one you can see. Codes: not_found.","content":{"application/problem+json":{"schema":{"$ref":"#/components/schemas/Problem"}}}},"429":{"description":"Too many requests. Codes: rate_limited.","content":{"application/problem+json":{"schema":{"$ref":"#/components/schemas/Problem"}}}},"500":{"description":"Something went wrong. Codes: internal_error.","content":{"application/problem+json":{"schema":{"$ref":"#/components/schemas/Problem"}}}}}},"post":{"operationId":"createScopeProbe","tags":["meta"],"summary":"Write an organisation scoping smoke row","description":"The organisation is taken from the path and written onto the row by the repository; a body cannot choose it. `X-Clavian-Venue` narrows the scope to one venue.","parameters":[{"schema":{"$ref":"#/components/schemas/OrganizationId"},"required":true,"description":"A TypeID: \"org_\" followed by the base32 form of a UUIDv7.","name":"organizationId","in":"path"}],"requestBody":{"required":true,"content":{"application/json":{"schema":{"$ref":"#/components/schemas/CreateScopeProbe"}}}},"responses":{"201":{"description":"The row that was written.","content":{"application/json":{"schema":{"$ref":"#/components/schemas/ScopeProbe"}}}},"400":{"description":"The request could not be understood. Codes: validation_failed.","content":{"application/problem+json":{"schema":{"$ref":"#/components/schemas/Problem"}}}},"401":{"description":"Not signed in. Codes: unauthenticated.","content":{"application/problem+json":{"schema":{"$ref":"#/components/schemas/Problem"}}}},"404":{"description":"No such organisation, or not one you can see. Codes: not_found.","content":{"application/problem+json":{"schema":{"$ref":"#/components/schemas/Problem"}}}},"429":{"description":"Too many requests. Codes: rate_limited.","content":{"application/problem+json":{"schema":{"$ref":"#/components/schemas/Problem"}}}},"500":{"description":"Something went wrong. Codes: internal_error.","content":{"application/problem+json":{"schema":{"$ref":"#/components/schemas/Problem"}}}}}}},"/v1/organizations/{organizationId}/scope-probes/{scopeProbeId}":{"get":{"operationId":"getScopeProbe","tags":["meta"],"summary":"Read one organisation scoping smoke row","description":"A row of another organisation is 404, not 403: revealing that it exists is itself the leak (spec/08 §2.6, spec/20 §11 rule 6).","parameters":[{"schema":{"$ref":"#/components/schemas/OrganizationId"},"required":true,"description":"A TypeID: \"org_\" followed by the base32 form of a UUIDv7.","name":"organizationId","in":"path"},{"schema":{"$ref":"#/components/schemas/ScopeProbeId"},"required":true,"description":"A TypeID: \"prb_\" followed by the base32 form of a UUIDv7.","name":"scopeProbeId","in":"path"}],"responses":{"200":{"description":"The row.","content":{"application/json":{"schema":{"$ref":"#/components/schemas/ScopeProbe"}}}},"400":{"description":"The request could not be understood. Codes: validation_failed.","content":{"application/problem+json":{"schema":{"$ref":"#/components/schemas/Problem"}}}},"401":{"description":"Not signed in. Codes: unauthenticated.","content":{"application/problem+json":{"schema":{"$ref":"#/components/schemas/Problem"}}}},"404":{"description":"No such row in this organisation. Codes: not_found.","content":{"application/problem+json":{"schema":{"$ref":"#/components/schemas/Problem"}}}},"429":{"description":"Too many requests. Codes: rate_limited.","content":{"application/problem+json":{"schema":{"$ref":"#/components/schemas/Problem"}}}},"500":{"description":"Something went wrong. Codes: internal_error.","content":{"application/problem+json":{"schema":{"$ref":"#/components/schemas/Problem"}}}}}}},"/v1/organizations/{organizationId}/audit":{"get":{"operationId":"listOrganizationAudit","tags":["audit"],"summary":"The organisation’s audit log","description":"Every staff and HQ action in this organisation that changed money, time, access or configuration, or read something sensitive, newest first. Personal data in `before` and `after` is masked. Needs `audit:read` (spec/07 §11).","x-clavian-permission":"audit:read","parameters":[{"schema":{"$ref":"#/components/schemas/OrganizationId"},"required":true,"description":"A TypeID: \"org_\" followed by the base32 form of a UUIDv7.","name":"organizationId","in":"path"},{"schema":{"type":"string","minLength":1,"description":"The `page.cursor` from the previous response."},"required":false,"description":"The `page.cursor` from the previous response.","name":"cursor","in":"query"},{"schema":{"type":"integer","minimum":1,"maximum":100,"default":25,"description":"Items per page, 1–100."},"required":false,"description":"Items per page, 1–100.","name":"limit","in":"query"},{"schema":{"type":"string","minLength":1,"maxLength":128,"description":"Only rows by this actor (a user, device or API key id)."},"required":false,"description":"Only rows by this actor (a user, device or API key id).","name":"actorId","in":"query"},{"schema":{"type":"string","enum":["user","member","device","api_key","system"],"description":"Only rows by this kind of actor."},"required":false,"description":"Only rows by this kind of actor.","name":"actorKind","in":"query"},{"schema":{"type":"string","maxLength":120,"pattern":"^[a-z][a-z0-9_]*(?:\\.[a-z][a-z0-9_]*)*(?:\\.\\*)?$","description":"An action (`sessions.comp`), or every action under a prefix (`sessions.*`)."},"required":false,"description":"An action (`sessions.comp`), or every action under a prefix (`sessions.*`).","name":"action","in":"query"},{"schema":{"type":"string","minLength":1,"maxLength":64,"pattern":"^[a-z][a-z0-9_]*$","description":"Only rows about this kind of thing: `player`, `setting_value`."},"required":false,"description":"Only rows about this kind of thing: `player`, `setting_value`.","name":"targetType","in":"query"},{"schema":{"type":"string","minLength":1,"maxLength":64,"description":"Only rows about this one thing: its uuid or its TypeID. A player’s Audit tab is this filter."},"required":false,"description":"Only rows about this one thing: its uuid or its TypeID. A player’s Audit tab is this filter.","name":"targetId","in":"query"},{"schema":{"allOf":[{"$ref":"#/components/schemas/VenueId"},{"description":"Only rows recorded against this venue."}]},"required":false,"description":"Only rows recorded against this venue.","name":"venueId","in":"query"},{"schema":{"type":"string","minLength":1,"maxLength":128,"description":"Only rows taken under this break-glass grant or impersonation (spec/07 §8)."},"required":false,"description":"Only rows taken under this break-glass grant or impersonation (spec/07 §8).","name":"onBehalfOf","in":"query"},{"schema":{"allOf":[{"$ref":"#/components/schemas/RequestId"},{"description":"Only rows written by this request — the `X-Request-Id` a caller quotes to support."}]},"required":false,"description":"Only rows written by this request — the `X-Request-Id` a caller quotes to support.","name":"requestId","in":"query"},{"schema":{"allOf":[{"$ref":"#/components/schemas/IsoDateTime"},{"description":"Only rows that occurred at or after this instant."}]},"required":false,"description":"Only rows that occurred at or after this instant.","name":"from","in":"query"},{"schema":{"allOf":[{"$ref":"#/components/schemas/IsoDateTime"},{"description":"Only rows that occurred before this instant."}]},"required":false,"description":"Only rows that occurred before this instant.","name":"to","in":"query"}],"responses":{"200":{"description":"A page of this organisation’s audit rows.","content":{"application/json":{"schema":{"$ref":"#/components/schemas/AuditEntryList"}}}},"400":{"description":"The request could not be understood. Codes: validation_failed.","content":{"application/problem+json":{"schema":{"$ref":"#/components/schemas/Problem"}}}},"401":{"description":"Not signed in. Codes: unauthenticated.","content":{"application/problem+json":{"schema":{"$ref":"#/components/schemas/Problem"}}}},"403":{"description":"Not allowed. Codes: forbidden, second_factor_required.","content":{"application/problem+json":{"schema":{"$ref":"#/components/schemas/Problem"}}}},"404":{"description":"No such organisation, or it is not one you can see. Codes: not_found.","content":{"application/problem+json":{"schema":{"$ref":"#/components/schemas/Problem"}}}},"429":{"description":"Too many requests. Codes: rate_limited.","content":{"application/problem+json":{"schema":{"$ref":"#/components/schemas/Problem"}}}},"500":{"description":"Something went wrong. Codes: internal_error.","content":{"application/problem+json":{"schema":{"$ref":"#/components/schemas/Problem"}}}}}}},"/v1/organizations/{organizationId}/audit/export":{"post":{"operationId":"exportOrganizationAudit","tags":["audit"],"summary":"Export the organisation’s audit log as CSV","description":"Exactly the rows the filter matches, as CSV, with every cell a spreadsheet would read as a formula neutralised. The export is itself recorded in the log. Needs `audit:read`.","x-clavian-permission":"audit:read","parameters":[{"schema":{"$ref":"#/components/schemas/OrganizationId"},"required":true,"description":"A TypeID: \"org_\" followed by the base32 form of a UUIDv7.","name":"organizationId","in":"path"}],"requestBody":{"required":true,"content":{"application/json":{"schema":{"$ref":"#/components/schemas/AuditExportRequest"}}}},"responses":{"200":{"description":"The filtered rows, as CSV.","headers":{"Content-Disposition":{"description":"attachment; filename=\"…csv\"","schema":{"type":"string"}}},"content":{"text/csv":{"schema":{"type":"string"}}}},"400":{"description":"The request could not be understood. Codes: validation_failed.","content":{"application/problem+json":{"schema":{"$ref":"#/components/schemas/Problem"}}}},"401":{"description":"Not signed in. Codes: unauthenticated.","content":{"application/problem+json":{"schema":{"$ref":"#/components/schemas/Problem"}}}},"403":{"description":"Not allowed. Codes: forbidden, second_factor_required.","content":{"application/problem+json":{"schema":{"$ref":"#/components/schemas/Problem"}}}},"404":{"description":"No such organisation, or it is not one you can see. Codes: not_found.","content":{"application/problem+json":{"schema":{"$ref":"#/components/schemas/Problem"}}}},"422":{"description":"The filter matches more than 10000 rows. Narrow it; an export is never truncated. Codes: unprocessable.","content":{"application/problem+json":{"schema":{"$ref":"#/components/schemas/Problem"}}}},"429":{"description":"Too many requests. Codes: rate_limited.","content":{"application/problem+json":{"schema":{"$ref":"#/components/schemas/Problem"}}}},"500":{"description":"Something went wrong. Codes: internal_error.","content":{"application/problem+json":{"schema":{"$ref":"#/components/schemas/Problem"}}}}}}},"/v1/hq/audit":{"get":{"operationId":"listPlatformAudit","tags":["audit","hq"],"summary":"The platform audit log","description":"Every organisation’s audit rows and the platform’s own, newest first. The read is audited: each organisation whose rows are returned gets a row in its own log naming you and your `reason`. Needs `audit:read` at platform scope.","x-clavian-permission":"audit:read","parameters":[{"schema":{"type":"string","minLength":1,"description":"The `page.cursor` from the previous response."},"required":false,"description":"The `page.cursor` from the previous response.","name":"cursor","in":"query"},{"schema":{"type":"integer","minimum":1,"maximum":100,"default":25,"description":"Items per page, 1–100."},"required":false,"description":"Items per page, 1–100.","name":"limit","in":"query"},{"schema":{"type":"string","minLength":1,"maxLength":128,"description":"Only rows by this actor (a user, device or API key id)."},"required":false,"description":"Only rows by this actor (a user, device or API key id).","name":"actorId","in":"query"},{"schema":{"type":"string","enum":["user","member","device","api_key","system"],"description":"Only rows by this kind of actor."},"required":false,"description":"Only rows by this kind of actor.","name":"actorKind","in":"query"},{"schema":{"type":"string","maxLength":120,"pattern":"^[a-z][a-z0-9_]*(?:\\.[a-z][a-z0-9_]*)*(?:\\.\\*)?$","description":"An action (`sessions.comp`), or every action under a prefix (`sessions.*`)."},"required":false,"description":"An action (`sessions.comp`), or every action under a prefix (`sessions.*`).","name":"action","in":"query"},{"schema":{"type":"string","minLength":1,"maxLength":64,"pattern":"^[a-z][a-z0-9_]*$","description":"Only rows about this kind of thing: `player`, `setting_value`."},"required":false,"description":"Only rows about this kind of thing: `player`, `setting_value`.","name":"targetType","in":"query"},{"schema":{"type":"string","minLength":1,"maxLength":64,"description":"Only rows about this one thing: its uuid or its TypeID. A player’s Audit tab is this filter."},"required":false,"description":"Only rows about this one thing: its uuid or its TypeID. A player’s Audit tab is this filter.","name":"targetId","in":"query"},{"schema":{"allOf":[{"$ref":"#/components/schemas/VenueId"},{"description":"Only rows recorded against this venue."}]},"required":false,"description":"Only rows recorded against this venue.","name":"venueId","in":"query"},{"schema":{"type":"string","minLength":1,"maxLength":128,"description":"Only rows taken under this break-glass grant or impersonation (spec/07 §8)."},"required":false,"description":"Only rows taken under this break-glass grant or impersonation (spec/07 §8).","name":"onBehalfOf","in":"query"},{"schema":{"allOf":[{"$ref":"#/components/schemas/RequestId"},{"description":"Only rows written by this request — the `X-Request-Id` a caller quotes to support."}]},"required":false,"description":"Only rows written by this request — the `X-Request-Id` a caller quotes to support.","name":"requestId","in":"query"},{"schema":{"allOf":[{"$ref":"#/components/schemas/IsoDateTime"},{"description":"Only rows that occurred at or after this instant."}]},"required":false,"description":"Only rows that occurred at or after this instant.","name":"from","in":"query"},{"schema":{"allOf":[{"$ref":"#/components/schemas/IsoDateTime"},{"description":"Only rows that occurred before this instant."}]},"required":false,"description":"Only rows that occurred before this instant.","name":"to","in":"query"},{"schema":{"allOf":[{"$ref":"#/components/schemas/OrganizationId"},{"description":"Only this organisation’s rows."}]},"required":false,"description":"Only this organisation’s rows.","name":"organizationId","in":"query"},{"schema":{"type":"string","minLength":3,"maxLength":500,"description":"Why you are reading. Recorded, with you, in the log of every organisation whose rows you see."},"required":true,"description":"Why you are reading. Recorded, with you, in the log of every organisation whose rows you see.","name":"reason","in":"query"}],"responses":{"200":{"description":"A page of audit rows.","content":{"application/json":{"schema":{"$ref":"#/components/schemas/AuditEntryList"}}}},"400":{"description":"The request could not be understood. Codes: validation_failed.","content":{"application/problem+json":{"schema":{"$ref":"#/components/schemas/Problem"}}}},"401":{"description":"Not signed in. Codes: unauthenticated.","content":{"application/problem+json":{"schema":{"$ref":"#/components/schemas/Problem"}}}},"403":{"description":"Not allowed. Codes: forbidden, second_factor_required.","content":{"application/problem+json":{"schema":{"$ref":"#/components/schemas/Problem"}}}},"429":{"description":"Too many requests. Codes: rate_limited.","content":{"application/problem+json":{"schema":{"$ref":"#/components/schemas/Problem"}}}},"500":{"description":"Something went wrong. Codes: internal_error.","content":{"application/problem+json":{"schema":{"$ref":"#/components/schemas/Problem"}}}}}}},"/v1/hq/audit/export":{"post":{"operationId":"exportPlatformAudit","tags":["audit","hq"],"summary":"Export the platform audit log as CSV","description":"The HQ view’s filtered set as CSV, audited like the read. Needs `audit:read` at platform scope.","x-clavian-permission":"audit:read","requestBody":{"required":true,"content":{"application/json":{"schema":{"$ref":"#/components/schemas/PlatformAuditExportRequest"}}}},"responses":{"200":{"description":"The filtered rows, as CSV.","headers":{"Content-Disposition":{"description":"attachment; filename=\"…csv\"","schema":{"type":"string"}}},"content":{"text/csv":{"schema":{"type":"string"}}}},"400":{"description":"The request could not be understood. Codes: validation_failed.","content":{"application/problem+json":{"schema":{"$ref":"#/components/schemas/Problem"}}}},"401":{"description":"Not signed in. Codes: unauthenticated.","content":{"application/problem+json":{"schema":{"$ref":"#/components/schemas/Problem"}}}},"403":{"description":"Not allowed. Codes: forbidden, second_factor_required.","content":{"application/problem+json":{"schema":{"$ref":"#/components/schemas/Problem"}}}},"422":{"description":"The filter matches more than 10000 rows. Narrow it; an export is never truncated. Codes: unprocessable.","content":{"application/problem+json":{"schema":{"$ref":"#/components/schemas/Problem"}}}},"429":{"description":"Too many requests. Codes: rate_limited.","content":{"application/problem+json":{"schema":{"$ref":"#/components/schemas/Problem"}}}},"500":{"description":"Something went wrong. Codes: internal_error.","content":{"application/problem+json":{"schema":{"$ref":"#/components/schemas/Problem"}}}}}}},"/v1/station/sign-in/phone/code":{"post":{"operationId":"requestStationPhoneCode","tags":["identity"],"summary":"Send a sign-in code to a phone, from a kiosk","description":"Station-authenticated: `Authorization: Bearer <kiosk token>` (spec/07 §4.1). A user session is not accepted here, and no better-auth session is created on the station. Sends a 6-digit code valid for 10 minutes by SMS or WhatsApp per `auth.otpChannel`. Numbers in premium-rate ranges are refused. A 503 means the code was not sent — never that it was.","requestBody":{"required":true,"content":{"application/json":{"schema":{"$ref":"#/components/schemas/StationPhoneCodeRequest"}}}},"responses":{"202":{"description":"The code has been handed to a provider.","content":{"application/json":{"schema":{"$ref":"#/components/schemas/StationPhoneCodeSent"}}}},"400":{"description":"The request could not be understood, or the number cannot be used. Codes: validation_failed, phone_number_refused.","content":{"application/problem+json":{"schema":{"$ref":"#/components/schemas/Problem"}}}},"401":{"description":"The caller is not an authenticated station. Codes: unauthenticated.","content":{"application/problem+json":{"schema":{"$ref":"#/components/schemas/Problem"}}}},"429":{"description":"Too many requests. Codes: rate_limited.","content":{"application/problem+json":{"schema":{"$ref":"#/components/schemas/Problem"}}}},"500":{"description":"Something went wrong. Codes: internal_error.","content":{"application/problem+json":{"schema":{"$ref":"#/components/schemas/Problem"}}}},"503":{"description":"No provider could send the code; nothing was sent. Codes: otp_delivery_unavailable.","content":{"application/problem+json":{"schema":{"$ref":"#/components/schemas/Problem"}}}}}}},"/v1/station/sign-in/phone":{"post":{"operationId":"signInAtStationWithPhone","tags":["identity"],"summary":"Sign a player in at a kiosk with a phone code","description":"Station-authenticated: `Authorization: Bearer <kiosk token>` (spec/07 §4.1). A user session is not accepted here, and no better-auth session is created on the station. Verifies the code and signs the player in, creating the account on first verification — only with the terms and privacy accepted in `consent`. A new number without them is refused with `consent-required` and keeps its code. Five wrong codes void the code; five failures in 15 minutes lock the account for 15 minutes.","requestBody":{"required":true,"content":{"application/json":{"schema":{"$ref":"#/components/schemas/StationPhoneSignIn"}}}},"responses":{"200":{"description":"The player is signed in on this station.","content":{"application/json":{"schema":{"$ref":"#/components/schemas/StationSignInResult"}}}},"400":{"description":"The code is wrong, expired or used up, or a new account was asked for without the terms and privacy accepted. Codes: validation_failed, otp_invalid, otp_expired, consent_required.","content":{"application/problem+json":{"schema":{"$ref":"#/components/schemas/Problem"}}}},"401":{"description":"The caller is not an authenticated station. Codes: unauthenticated.","content":{"application/problem+json":{"schema":{"$ref":"#/components/schemas/Problem"}}}},"403":{"description":"The player is banned here — at this venue, across its organisation, or from the Platform. The detail is \"Please see the front desk.\" whichever it is, and never the reason. Codes: player_banned.","content":{"application/problem+json":{"schema":{"$ref":"#/components/schemas/Problem"}}}},"409":{"description":"The terms or privacy version shown is not the one in force. Codes: consent_version_outdated.","content":{"application/problem+json":{"schema":{"$ref":"#/components/schemas/Problem"}}}},"429":{"description":"Too many requests. Codes: rate_limited.","content":{"application/problem+json":{"schema":{"$ref":"#/components/schemas/Problem"}}}},"500":{"description":"Something went wrong. Codes: internal_error.","content":{"application/problem+json":{"schema":{"$ref":"#/components/schemas/Problem"}}}}}}},"/v1/station/sign-in/password":{"post":{"operationId":"signInAtStationWithPassword","tags":["identity"],"summary":"Sign a player in at a kiosk with an email address or username and a password","description":"Station-authenticated: `Authorization: Bearer <kiosk token>` (spec/07 §4.1). A user session is not accepted here, and no better-auth session is created on the station. An unknown account, a wrong password and a malformed username all answer `invalid-credentials`, and a failure takes as long as any other. Five failures in 15 minutes lock the account for 15 minutes, here and everywhere else.","requestBody":{"required":true,"content":{"application/json":{"schema":{"$ref":"#/components/schemas/StationPasswordSignIn"}}}},"responses":{"200":{"description":"The player is signed in on this station.","content":{"application/json":{"schema":{"$ref":"#/components/schemas/StationSignInResult"}}}},"400":{"description":"The request could not be understood. Codes: validation_failed.","content":{"application/problem+json":{"schema":{"$ref":"#/components/schemas/Problem"}}}},"401":{"description":"Not an authenticated station, or the credentials are wrong. Codes: unauthenticated, invalid_credentials.","content":{"application/problem+json":{"schema":{"$ref":"#/components/schemas/Problem"}}}},"403":{"description":"The password is right but the email address has not been verified; or: The player is banned here — at this venue, across its organisation, or from the Platform. The detail is \"Please see the front desk.\" whichever it is, and never the reason. Codes: email_not_verified, player_banned.","content":{"application/problem+json":{"schema":{"$ref":"#/components/schemas/Problem"}}}},"429":{"description":"Too many requests. Codes: rate_limited.","content":{"application/problem+json":{"schema":{"$ref":"#/components/schemas/Problem"}}}},"500":{"description":"Something went wrong. Codes: internal_error.","content":{"application/problem+json":{"schema":{"$ref":"#/components/schemas/Problem"}}}}}}},"/v1/organizations/{organizationId}/venues":{"get":{"operationId":"listVenues","tags":["venues"],"summary":"List the organisation's venues","description":"Archived venues are left out, and a member who works at some venues only is shown only those (spec/07 §6.1). Requires `venues:read`.","x-clavian-permission":"venues:read","parameters":[{"schema":{"$ref":"#/components/schemas/OrganizationId"},"required":true,"description":"A TypeID: \"org_\" followed by the base32 form of a UUIDv7.","name":"organizationId","in":"path"},{"schema":{"type":"string","minLength":1,"description":"The `page.cursor` from the previous response."},"required":false,"description":"The `page.cursor` from the previous response.","name":"cursor","in":"query"},{"schema":{"type":"integer","minimum":1,"maximum":100,"default":25,"description":"Items per page, 1–100."},"required":false,"description":"Items per page, 1–100.","name":"limit","in":"query"}],"responses":{"200":{"description":"A page of venues.","content":{"application/json":{"schema":{"$ref":"#/components/schemas/VenueList"}}}},"400":{"description":"The request could not be understood. Codes: validation_failed.","content":{"application/problem+json":{"schema":{"$ref":"#/components/schemas/Problem"}}}},"401":{"description":"Not signed in. Codes: unauthenticated.","content":{"application/problem+json":{"schema":{"$ref":"#/components/schemas/Problem"}}}},"403":{"description":"Your role does not allow this. Codes: forbidden, second_factor_required.","content":{"application/problem+json":{"schema":{"$ref":"#/components/schemas/Problem"}}}},"404":{"description":"No such organisation, or not one you can see. Codes: not_found.","content":{"application/problem+json":{"schema":{"$ref":"#/components/schemas/Problem"}}}},"429":{"description":"Too many requests. Codes: rate_limited.","content":{"application/problem+json":{"schema":{"$ref":"#/components/schemas/Problem"}}}},"500":{"description":"Something went wrong. Codes: internal_error.","content":{"application/problem+json":{"schema":{"$ref":"#/components/schemas/Problem"}}}}}},"post":{"operationId":"createVenue","tags":["venues"],"summary":"Create a venue","description":"Starts in `setup`. The currency must be the organisation's (omit it to take the organisation's). Requires `venues:create`.","x-clavian-permission":"venues:create","parameters":[{"schema":{"$ref":"#/components/schemas/OrganizationId"},"required":true,"description":"A TypeID: \"org_\" followed by the base32 form of a UUIDv7.","name":"organizationId","in":"path"}],"requestBody":{"required":true,"content":{"application/json":{"schema":{"$ref":"#/components/schemas/CreateVenue"}}}},"responses":{"201":{"description":"The venue that was created.","headers":{"ETag":{"schema":{"type":"string","description":"The version to send back as If-Match."},"required":true,"description":"The version to send back as If-Match."}},"content":{"application/json":{"schema":{"$ref":"#/components/schemas/Venue"}}}},"400":{"description":"The request could not be understood. Codes: validation_failed.","content":{"application/problem+json":{"schema":{"$ref":"#/components/schemas/Problem"}}}},"401":{"description":"Not signed in. Codes: unauthenticated.","content":{"application/problem+json":{"schema":{"$ref":"#/components/schemas/Problem"}}}},"403":{"description":"Your role does not allow this. Codes: forbidden, second_factor_required.","content":{"application/problem+json":{"schema":{"$ref":"#/components/schemas/Problem"}}}},"404":{"description":"No such organisation, or not one you can see. Codes: not_found.","content":{"application/problem+json":{"schema":{"$ref":"#/components/schemas/Problem"}}}},"409":{"description":"Another venue already has that slug. Codes: slug_taken.","content":{"application/problem+json":{"schema":{"$ref":"#/components/schemas/Problem"}}}},"422":{"description":"The currency is not the organisation's. Codes: currency_mismatch.","content":{"application/problem+json":{"schema":{"$ref":"#/components/schemas/Problem"}}}},"429":{"description":"Too many requests. Codes: rate_limited.","content":{"application/problem+json":{"schema":{"$ref":"#/components/schemas/Problem"}}}},"500":{"description":"Something went wrong. Codes: internal_error.","content":{"application/problem+json":{"schema":{"$ref":"#/components/schemas/Problem"}}}}}}},"/v1/organizations/{organizationId}/venues/{venueId}":{"get":{"operationId":"getVenue","tags":["venues"],"summary":"Read a venue","description":"Returns the venue and its `ETag`. Requires `venues:read`.","x-clavian-permission":"venues:read","parameters":[{"schema":{"$ref":"#/components/schemas/OrganizationId"},"required":true,"description":"A TypeID: \"org_\" followed by the base32 form of a UUIDv7.","name":"organizationId","in":"path"},{"schema":{"$ref":"#/components/schemas/VenueId"},"required":true,"description":"A TypeID: \"ven_\" followed by the base32 form of a UUIDv7.","name":"venueId","in":"path"}],"responses":{"200":{"description":"The venue.","headers":{"ETag":{"schema":{"type":"string","description":"The version to send back as If-Match."},"required":true,"description":"The version to send back as If-Match."}},"content":{"application/json":{"schema":{"$ref":"#/components/schemas/Venue"}}}},"400":{"description":"The request could not be understood. Codes: validation_failed.","content":{"application/problem+json":{"schema":{"$ref":"#/components/schemas/Problem"}}}},"401":{"description":"Not signed in. Codes: unauthenticated.","content":{"application/problem+json":{"schema":{"$ref":"#/components/schemas/Problem"}}}},"403":{"description":"Your role does not allow this. Codes: forbidden, second_factor_required.","content":{"application/problem+json":{"schema":{"$ref":"#/components/schemas/Problem"}}}},"404":{"description":"No such venue in this organisation, or not one you can see. Codes: not_found.","content":{"application/problem+json":{"schema":{"$ref":"#/components/schemas/Problem"}}}},"429":{"description":"Too many requests. Codes: rate_limited.","content":{"application/problem+json":{"schema":{"$ref":"#/components/schemas/Problem"}}}},"500":{"description":"Something went wrong. Codes: internal_error.","content":{"application/problem+json":{"schema":{"$ref":"#/components/schemas/Problem"}}}}}},"patch":{"operationId":"updateVenue","tags":["venues"],"summary":"Edit a venue","description":"Send the `ETag` you read as `If-Match`. A stale one is 412 and nothing is written; read the venue again and re-apply. `timezone` and `businessDayCutoff` change only while the venue is in `setup`. Requires `venues:update`. Every change is audited with the before and after.","x-clavian-permission":"venues:update","parameters":[{"schema":{"$ref":"#/components/schemas/OrganizationId"},"required":true,"description":"A TypeID: \"org_\" followed by the base32 form of a UUIDv7.","name":"organizationId","in":"path"},{"schema":{"$ref":"#/components/schemas/VenueId"},"required":true,"description":"A TypeID: \"ven_\" followed by the base32 form of a UUIDv7.","name":"venueId","in":"path"},{"schema":{"type":"string","minLength":1,"maxLength":64,"description":"The ETag this write is based on."},"required":true,"description":"The ETag this write is based on.","name":"if-match","in":"header"}],"requestBody":{"required":true,"content":{"application/json":{"schema":{"$ref":"#/components/schemas/UpdateVenue"}}}},"responses":{"200":{"description":"The venue as it is now.","headers":{"ETag":{"schema":{"type":"string","description":"The version to send back as If-Match."},"required":true,"description":"The version to send back as If-Match."}},"content":{"application/json":{"schema":{"$ref":"#/components/schemas/Venue"}}}},"400":{"description":"The request could not be understood. Codes: validation_failed.","content":{"application/problem+json":{"schema":{"$ref":"#/components/schemas/Problem"}}}},"401":{"description":"Not signed in. Codes: unauthenticated.","content":{"application/problem+json":{"schema":{"$ref":"#/components/schemas/Problem"}}}},"403":{"description":"Your role does not allow this. Codes: forbidden, second_factor_required.","content":{"application/problem+json":{"schema":{"$ref":"#/components/schemas/Problem"}}}},"404":{"description":"No such venue in this organisation, or not one you can see. Codes: not_found.","content":{"application/problem+json":{"schema":{"$ref":"#/components/schemas/Problem"}}}},"409":{"description":"The slug is taken, or the change is not allowed in this status. Codes: slug_taken, conflict.","content":{"application/problem+json":{"schema":{"$ref":"#/components/schemas/Problem"}}}},"412":{"description":"Someone else changed the venue since you read it. Codes: stale_write.","content":{"application/problem+json":{"schema":{"$ref":"#/components/schemas/Problem"}}}},"429":{"description":"Too many requests. Codes: rate_limited.","content":{"application/problem+json":{"schema":{"$ref":"#/components/schemas/Problem"}}}},"500":{"description":"Something went wrong. Codes: internal_error.","content":{"application/problem+json":{"schema":{"$ref":"#/components/schemas/Problem"}}}}}}},"/v1/organizations/{organizationId}/venues/{venueId}/archive":{"post":{"operationId":"archiveVenue","tags":["venues"],"summary":"Close a venue for good","description":"Sets the status to `closed`, takes it out of the public listing and out of the venue list. Nothing is deleted: its orders and sessions are financial records (spec/04 §1.4). Requires `venues:archive` and `If-Match`.","x-clavian-permission":"venues:archive","parameters":[{"schema":{"$ref":"#/components/schemas/OrganizationId"},"required":true,"description":"A TypeID: \"org_\" followed by the base32 form of a UUIDv7.","name":"organizationId","in":"path"},{"schema":{"$ref":"#/components/schemas/VenueId"},"required":true,"description":"A TypeID: \"ven_\" followed by the base32 form of a UUIDv7.","name":"venueId","in":"path"},{"schema":{"type":"string","minLength":1,"maxLength":64,"description":"The ETag this write is based on."},"required":true,"description":"The ETag this write is based on.","name":"if-match","in":"header"}],"responses":{"200":{"description":"The archived venue.","headers":{"ETag":{"schema":{"type":"string","description":"The version to send back as If-Match."},"required":true,"description":"The version to send back as If-Match."}},"content":{"application/json":{"schema":{"$ref":"#/components/schemas/Venue"}}}},"400":{"description":"The request could not be understood. Codes: validation_failed.","content":{"application/problem+json":{"schema":{"$ref":"#/components/schemas/Problem"}}}},"401":{"description":"Not signed in. Codes: unauthenticated.","content":{"application/problem+json":{"schema":{"$ref":"#/components/schemas/Problem"}}}},"403":{"description":"Your role does not allow this. Codes: forbidden, second_factor_required.","content":{"application/problem+json":{"schema":{"$ref":"#/components/schemas/Problem"}}}},"404":{"description":"No such venue in this organisation, or not one you can see. Codes: not_found.","content":{"application/problem+json":{"schema":{"$ref":"#/components/schemas/Problem"}}}},"409":{"description":"The venue is already archived. Codes: conflict.","content":{"application/problem+json":{"schema":{"$ref":"#/components/schemas/Problem"}}}},"412":{"description":"Someone else changed the venue since you read it. Codes: stale_write.","content":{"application/problem+json":{"schema":{"$ref":"#/components/schemas/Problem"}}}},"429":{"description":"Too many requests. Codes: rate_limited.","content":{"application/problem+json":{"schema":{"$ref":"#/components/schemas/Problem"}}}},"500":{"description":"Something went wrong. Codes: internal_error.","content":{"application/problem+json":{"schema":{"$ref":"#/components/schemas/Problem"}}}}}}},"/v1/hq/organizations/{organizationId}/venues/{venueId}/listing":{"patch":{"operationId":"updateVenueListing","tags":["hq"],"summary":"Set a venue’s discovery flags","description":"Featured, Partner and Sponsored order the player app’s Venues list. Only the flags sent change, and nobody but HQ sets them, so there is no other editor to race and no `If-Match`; the venue’s ETag still moves, because its representation did. Platform scope: requires `hq:organizations` and a second factor. Reaching into the organisation is audited in its own log.","x-clavian-permission":"hq:organizations","parameters":[{"schema":{"$ref":"#/components/schemas/OrganizationId"},"required":true,"description":"A TypeID: \"org_\" followed by the base32 form of a UUIDv7.","name":"organizationId","in":"path"},{"schema":{"$ref":"#/components/schemas/VenueId"},"required":true,"description":"A TypeID: \"ven_\" followed by the base32 form of a UUIDv7.","name":"venueId","in":"path"}],"requestBody":{"required":true,"content":{"application/json":{"schema":{"$ref":"#/components/schemas/UpdateVenueListing"}}}},"responses":{"200":{"description":"The venue as it is now.","headers":{"ETag":{"schema":{"type":"string","description":"The version to send back as If-Match."},"required":true,"description":"The version to send back as If-Match."}},"content":{"application/json":{"schema":{"$ref":"#/components/schemas/Venue"}}}},"400":{"description":"The request could not be understood. Codes: validation_failed.","content":{"application/problem+json":{"schema":{"$ref":"#/components/schemas/Problem"}}}},"401":{"description":"Not signed in. Codes: unauthenticated.","content":{"application/problem+json":{"schema":{"$ref":"#/components/schemas/Problem"}}}},"403":{"description":"Your role does not allow this. Codes: forbidden, second_factor_required.","content":{"application/problem+json":{"schema":{"$ref":"#/components/schemas/Problem"}}}},"404":{"description":"No such venue in that organisation. Codes: not_found.","content":{"application/problem+json":{"schema":{"$ref":"#/components/schemas/Problem"}}}},"429":{"description":"Too many requests. Codes: rate_limited.","content":{"application/problem+json":{"schema":{"$ref":"#/components/schemas/Problem"}}}},"500":{"description":"Something went wrong. Codes: internal_error.","content":{"application/problem+json":{"schema":{"$ref":"#/components/schemas/Problem"}}}}}}},"/v1/hq/venues":{"get":{"operationId":"listHqVenues","tags":["hq"],"summary":"Every venue on the Platform","description":"Every organisation’s venues, archived ones included, oldest first, with their listing flags. Filters: `organizationId`, `q` (name or slug), `city`, `status`, and each flag. The read is audited: one platform-level row with the filter, and one row in the log of every organisation whose venues are on the page. Platform scope: requires `hq:organizations` and a second factor.","x-clavian-permission":"hq:organizations","parameters":[{"schema":{"type":"string","minLength":1,"description":"The `page.cursor` from the previous response."},"required":false,"description":"The `page.cursor` from the previous response.","name":"cursor","in":"query"},{"schema":{"type":"integer","minimum":1,"maximum":100,"default":25,"description":"Items per page, 1–100."},"required":false,"description":"Items per page, 1–100.","name":"limit","in":"query"},{"schema":{"allOf":[{"$ref":"#/components/schemas/OrganizationId"},{"description":"Only this organisation’s venues."}]},"required":false,"description":"Only this organisation’s venues.","name":"organizationId","in":"query"},{"schema":{"type":"string","minLength":2,"maxLength":120,"description":"Part of the venue’s name or slug, in any case."},"required":false,"description":"Part of the venue’s name or slug, in any case.","name":"q","in":"query"},{"schema":{"type":"string","minLength":1,"maxLength":80,"description":"The address locality, case-insensitive."},"required":false,"description":"The address locality, case-insensitive.","name":"city","in":"query"},{"schema":{"type":"string","pattern":"^(?:setup|live|paused|closed)(?:,(?:setup|live|paused|closed))*$","description":"Comma-separated; a venue in any of them.","example":"setup,live,paused"},"required":false,"description":"Comma-separated; a venue in any of them.","name":"status","in":"query"},{"schema":{"type":"string","enum":["true","false"]},"required":false,"name":"publicListing","in":"query"},{"schema":{"type":"string","enum":["true","false"]},"required":false,"name":"featured","in":"query"},{"schema":{"type":"string","enum":["true","false"]},"required":false,"name":"partner","in":"query"},{"schema":{"type":"string","enum":["true","false"]},"required":false,"name":"sponsored","in":"query"}],"responses":{"200":{"description":"A page of venues.","content":{"application/json":{"schema":{"$ref":"#/components/schemas/HqVenueList"}}}},"400":{"description":"The request could not be understood. Codes: validation_failed.","content":{"application/problem+json":{"schema":{"$ref":"#/components/schemas/Problem"}}}},"401":{"description":"Not signed in. Codes: unauthenticated.","content":{"application/problem+json":{"schema":{"$ref":"#/components/schemas/Problem"}}}},"403":{"description":"Your role does not allow this. Codes: forbidden, second_factor_required.","content":{"application/problem+json":{"schema":{"$ref":"#/components/schemas/Problem"}}}},"429":{"description":"Too many requests. Codes: rate_limited.","content":{"application/problem+json":{"schema":{"$ref":"#/components/schemas/Problem"}}}},"500":{"description":"Something went wrong. Codes: internal_error.","content":{"application/problem+json":{"schema":{"$ref":"#/components/schemas/Problem"}}}}}}},"/v1/public/venues":{"get":{"operationId":"listPublicVenues","tags":["public"],"summary":"Find venues","description":"Unauthenticated. Only listed, live venues. Ordered Featured, then Sponsored, then Partner, then by name. Filters: `city`, or `lat`/`lng`/`radiusKm`; `amenities`; `openNow`.","parameters":[{"schema":{"type":"string","minLength":1,"maxLength":80,"description":"The address locality, case-insensitive."},"required":false,"description":"The address locality, case-insensitive.","name":"city","in":"query"},{"schema":{"type":["number","null"],"minimum":-90,"maximum":90},"required":false,"name":"lat","in":"query"},{"schema":{"type":["number","null"],"minimum":-180,"maximum":180},"required":false,"name":"lng","in":"query"},{"schema":{"type":"integer","minimum":1,"maximum":200,"description":"Whole kilometres, with lat and lng. Default 10."},"required":false,"description":"Whole kilometres, with lat and lng. Default 10.","name":"radiusKm","in":"query"},{"schema":{"type":"string","enum":["true","false"]},"required":false,"name":"openNow","in":"query"},{"schema":{"type":"string","pattern":"^(?:parking|air_conditioning|food|washrooms|streaming_room)(?:,(?:parking|air_conditioning|food|washrooms|streaming_room))*$","description":"Comma-separated; every one must be offered.","example":"parking,food"},"required":false,"description":"Comma-separated; every one must be offered.","name":"amenities","in":"query"},{"schema":{"type":"string","minLength":1},"required":false,"name":"cursor","in":"query"},{"schema":{"type":"integer","minimum":1,"maximum":100,"default":25},"required":false,"name":"limit","in":"query"}],"responses":{"200":{"description":"A page of listed venues.","headers":{"Cache-Control":{"schema":{"type":"string"},"required":true}},"content":{"application/json":{"schema":{"$ref":"#/components/schemas/PublicVenueList"}}}},"400":{"description":"The request could not be understood. Codes: validation_failed.","content":{"application/problem+json":{"schema":{"$ref":"#/components/schemas/Problem"}}}},"429":{"description":"Too many requests. Codes: rate_limited.","content":{"application/problem+json":{"schema":{"$ref":"#/components/schemas/Problem"}}}},"500":{"description":"Something went wrong. Codes: internal_error.","content":{"application/problem+json":{"schema":{"$ref":"#/components/schemas/Problem"}}}}}}},"/v1/public/venues/{slug}":{"get":{"operationId":"getPublicVenue","tags":["public"],"summary":"A venue page","description":"Unauthenticated. A venue that is not listed, or not live, is 404 — the same as one that does not exist.","parameters":[{"schema":{"$ref":"#/components/schemas/VenueSlug"},"required":true,"name":"slug","in":"path"}],"responses":{"200":{"description":"The venue’s public listing.","headers":{"Cache-Control":{"schema":{"type":"string"},"required":true}},"content":{"application/json":{"schema":{"$ref":"#/components/schemas/PublicVenue"}}}},"400":{"description":"The request could not be understood. Codes: validation_failed.","content":{"application/problem+json":{"schema":{"$ref":"#/components/schemas/Problem"}}}},"404":{"description":"No listed venue with that slug. Codes: not_found.","content":{"application/problem+json":{"schema":{"$ref":"#/components/schemas/Problem"}}}},"429":{"description":"Too many requests. Codes: rate_limited.","content":{"application/problem+json":{"schema":{"$ref":"#/components/schemas/Problem"}}}},"500":{"description":"Something went wrong. Codes: internal_error.","content":{"application/problem+json":{"schema":{"$ref":"#/components/schemas/Problem"}}}}}}},"/v1/organizations/{organizationId}/files":{"post":{"operationId":"createFile","tags":["files"],"summary":"Start an upload","description":"Reserves the file and returns a signed URL, valid for 15 minutes, to PUT the bytes to. Complete it within the hour: a file still `pending` an hour after it started is marked `failed` and whatever was uploaded to it is removed. Requires `venues:update`. A `venue_photo` names a venue of this organisation. An `organization_logo` names none, is at most 2 MB, and also requires `settings:kiosk` from a member whose staff access covers every venue.","x-clavian-permission":"venues:update","x-clavian-purpose-permission":{"organization_logo":"settings:kiosk"},"parameters":[{"schema":{"$ref":"#/components/schemas/OrganizationId"},"required":true,"description":"A TypeID: \"org_\" followed by the base32 form of a UUIDv7.","name":"organizationId","in":"path"}],"requestBody":{"required":true,"content":{"application/json":{"schema":{"$ref":"#/components/schemas/CreateFile"}}}},"responses":{"201":{"description":"The file, pending, and where to upload it.","content":{"application/json":{"schema":{"$ref":"#/components/schemas/FileUpload"}}}},"400":{"description":"The request could not be understood. Codes: validation_failed.","content":{"application/problem+json":{"schema":{"$ref":"#/components/schemas/Problem"}}}},"401":{"description":"Not signed in. Codes: unauthenticated.","content":{"application/problem+json":{"schema":{"$ref":"#/components/schemas/Problem"}}}},"403":{"description":"Your role does not allow this. Codes: forbidden, second_factor_required.","content":{"application/problem+json":{"schema":{"$ref":"#/components/schemas/Problem"}}}},"404":{"description":"No such venue in this organisation. Codes: not_found.","content":{"application/problem+json":{"schema":{"$ref":"#/components/schemas/Problem"}}}},"429":{"description":"Too many requests. Codes: rate_limited.","content":{"application/problem+json":{"schema":{"$ref":"#/components/schemas/Problem"}}}},"500":{"description":"Something went wrong. Codes: internal_error.","content":{"application/problem+json":{"schema":{"$ref":"#/components/schemas/Problem"}}}}}}},"/v1/organizations/{organizationId}/files/{fileId}/complete":{"post":{"operationId":"completeFile","tags":["files"],"summary":"Finish an upload","description":"Checks the object is in storage with the declared size and type and is really an image, then hands it to the worker to make its variants. Completing twice is harmless. Requires what starting the upload did: `venues:update`, and `settings:kiosk` for an `organization_logo`.","x-clavian-permission":"venues:update","x-clavian-purpose-permission":{"organization_logo":"settings:kiosk"},"parameters":[{"schema":{"$ref":"#/components/schemas/OrganizationId"},"required":true,"description":"A TypeID: \"org_\" followed by the base32 form of a UUIDv7.","name":"organizationId","in":"path"},{"schema":{"$ref":"#/components/schemas/FileId"},"required":true,"description":"A TypeID: \"fil_\" followed by the base32 form of a UUIDv7.","name":"fileId","in":"path"}],"responses":{"200":{"description":"The file, uploaded.","content":{"application/json":{"schema":{"$ref":"#/components/schemas/File"}}}},"400":{"description":"The request could not be understood. Codes: validation_failed.","content":{"application/problem+json":{"schema":{"$ref":"#/components/schemas/Problem"}}}},"401":{"description":"Not signed in. Codes: unauthenticated.","content":{"application/problem+json":{"schema":{"$ref":"#/components/schemas/Problem"}}}},"403":{"description":"Your role does not allow this. Codes: forbidden, second_factor_required.","content":{"application/problem+json":{"schema":{"$ref":"#/components/schemas/Problem"}}}},"404":{"description":"No such file in this organisation. Codes: not_found.","content":{"application/problem+json":{"schema":{"$ref":"#/components/schemas/Problem"}}}},"422":{"description":"Nothing was uploaded, or it is not what was declared. Codes: unprocessable.","content":{"application/problem+json":{"schema":{"$ref":"#/components/schemas/Problem"}}}},"429":{"description":"Too many requests. Codes: rate_limited.","content":{"application/problem+json":{"schema":{"$ref":"#/components/schemas/Problem"}}}},"500":{"description":"Something went wrong. Codes: internal_error.","content":{"application/problem+json":{"schema":{"$ref":"#/components/schemas/Problem"}}}}}}},"/v1/organizations/{organizationId}/files/{fileId}":{"get":{"operationId":"getFile","tags":["files"],"summary":"Read a file","description":"Its status and, once the worker has run, its variants. Requires `venues:read`.","x-clavian-permission":"venues:read","parameters":[{"schema":{"$ref":"#/components/schemas/OrganizationId"},"required":true,"description":"A TypeID: \"org_\" followed by the base32 form of a UUIDv7.","name":"organizationId","in":"path"},{"schema":{"$ref":"#/components/schemas/FileId"},"required":true,"description":"A TypeID: \"fil_\" followed by the base32 form of a UUIDv7.","name":"fileId","in":"path"}],"responses":{"200":{"description":"The file.","content":{"application/json":{"schema":{"$ref":"#/components/schemas/File"}}}},"400":{"description":"The request could not be understood. Codes: validation_failed.","content":{"application/problem+json":{"schema":{"$ref":"#/components/schemas/Problem"}}}},"401":{"description":"Not signed in. Codes: unauthenticated.","content":{"application/problem+json":{"schema":{"$ref":"#/components/schemas/Problem"}}}},"403":{"description":"Your role does not allow this. Codes: forbidden, second_factor_required.","content":{"application/problem+json":{"schema":{"$ref":"#/components/schemas/Problem"}}}},"404":{"description":"No such file in this organisation. Codes: not_found.","content":{"application/problem+json":{"schema":{"$ref":"#/components/schemas/Problem"}}}},"429":{"description":"Too many requests. Codes: rate_limited.","content":{"application/problem+json":{"schema":{"$ref":"#/components/schemas/Problem"}}}},"500":{"description":"Something went wrong. Codes: internal_error.","content":{"application/problem+json":{"schema":{"$ref":"#/components/schemas/Problem"}}}}}}},"/v1/organizations/{organizationId}/venues/{venueId}/station-classes":{"get":{"operationId":"listStationClasses","tags":["stations"],"summary":"List a venue's station classes","description":"Oldest first. Requires `stations:read`.","x-clavian-permission":"stations:read","parameters":[{"schema":{"$ref":"#/components/schemas/OrganizationId"},"required":true,"description":"A TypeID: \"org_\" followed by the base32 form of a UUIDv7.","name":"organizationId","in":"path"},{"schema":{"$ref":"#/components/schemas/VenueId"},"required":true,"description":"A TypeID: \"ven_\" followed by the base32 form of a UUIDv7.","name":"venueId","in":"path"},{"schema":{"type":"string","minLength":1,"description":"The `page.cursor` from the previous response."},"required":false,"description":"The `page.cursor` from the previous response.","name":"cursor","in":"query"},{"schema":{"type":"integer","minimum":1,"maximum":100,"default":25,"description":"Items per page, 1–100."},"required":false,"description":"Items per page, 1–100.","name":"limit","in":"query"}],"responses":{"200":{"description":"A page of classes.","content":{"application/json":{"schema":{"$ref":"#/components/schemas/StationClassList"}}}},"400":{"description":"The request could not be understood. Codes: validation_failed.","content":{"application/problem+json":{"schema":{"$ref":"#/components/schemas/Problem"}}}},"401":{"description":"Not signed in. Codes: unauthenticated.","content":{"application/problem+json":{"schema":{"$ref":"#/components/schemas/Problem"}}}},"403":{"description":"Your role does not allow this. Codes: forbidden, second_factor_required.","content":{"application/problem+json":{"schema":{"$ref":"#/components/schemas/Problem"}}}},"404":{"description":"No such venue in this organisation, or not one you can see. Codes: not_found.","content":{"application/problem+json":{"schema":{"$ref":"#/components/schemas/Problem"}}}},"429":{"description":"Too many requests. Codes: rate_limited.","content":{"application/problem+json":{"schema":{"$ref":"#/components/schemas/Problem"}}}},"500":{"description":"Something went wrong. Codes: internal_error.","content":{"application/problem+json":{"schema":{"$ref":"#/components/schemas/Problem"}}}}}},"post":{"operationId":"createStationClass","tags":["stations"],"summary":"Create a station class","description":"Requires `stations:manage`. Audited.","x-clavian-permission":"stations:manage","parameters":[{"schema":{"$ref":"#/components/schemas/OrganizationId"},"required":true,"description":"A TypeID: \"org_\" followed by the base32 form of a UUIDv7.","name":"organizationId","in":"path"},{"schema":{"$ref":"#/components/schemas/VenueId"},"required":true,"description":"A TypeID: \"ven_\" followed by the base32 form of a UUIDv7.","name":"venueId","in":"path"}],"requestBody":{"required":true,"content":{"application/json":{"schema":{"$ref":"#/components/schemas/CreateStationClass"}}}},"responses":{"201":{"description":"The class that was created.","content":{"application/json":{"schema":{"$ref":"#/components/schemas/StationClass"}}}},"400":{"description":"The request could not be understood. Codes: validation_failed.","content":{"application/problem+json":{"schema":{"$ref":"#/components/schemas/Problem"}}}},"401":{"description":"Not signed in. Codes: unauthenticated.","content":{"application/problem+json":{"schema":{"$ref":"#/components/schemas/Problem"}}}},"403":{"description":"Your role does not allow this. Codes: forbidden, second_factor_required.","content":{"application/problem+json":{"schema":{"$ref":"#/components/schemas/Problem"}}}},"404":{"description":"No such venue in this organisation, or not one you can see. Codes: not_found.","content":{"application/problem+json":{"schema":{"$ref":"#/components/schemas/Problem"}}}},"409":{"description":"Another class at this venue already has that name. Codes: conflict.","content":{"application/problem+json":{"schema":{"$ref":"#/components/schemas/Problem"}}}},"429":{"description":"Too many requests. Codes: rate_limited.","content":{"application/problem+json":{"schema":{"$ref":"#/components/schemas/Problem"}}}},"500":{"description":"Something went wrong. Codes: internal_error.","content":{"application/problem+json":{"schema":{"$ref":"#/components/schemas/Problem"}}}}}}},"/v1/organizations/{organizationId}/venues/{venueId}/station-classes/{classId}":{"patch":{"operationId":"updateStationClass","tags":["stations"],"summary":"Edit a station class","description":"Only the fields sent change. Requires `stations:manage`. Audited with the before and after.","x-clavian-permission":"stations:manage","parameters":[{"schema":{"$ref":"#/components/schemas/OrganizationId"},"required":true,"description":"A TypeID: \"org_\" followed by the base32 form of a UUIDv7.","name":"organizationId","in":"path"},{"schema":{"$ref":"#/components/schemas/VenueId"},"required":true,"description":"A TypeID: \"ven_\" followed by the base32 form of a UUIDv7.","name":"venueId","in":"path"},{"schema":{"$ref":"#/components/schemas/StationClassId"},"required":true,"description":"A TypeID: \"scl_\" followed by the base32 form of a UUIDv7.","name":"classId","in":"path"}],"requestBody":{"required":true,"content":{"application/json":{"schema":{"$ref":"#/components/schemas/UpdateStationClass"}}}},"responses":{"200":{"description":"The class as it is now.","content":{"application/json":{"schema":{"$ref":"#/components/schemas/StationClass"}}}},"400":{"description":"The request could not be understood. Codes: validation_failed.","content":{"application/problem+json":{"schema":{"$ref":"#/components/schemas/Problem"}}}},"401":{"description":"Not signed in. Codes: unauthenticated.","content":{"application/problem+json":{"schema":{"$ref":"#/components/schemas/Problem"}}}},"403":{"description":"Your role does not allow this. Codes: forbidden, second_factor_required.","content":{"application/problem+json":{"schema":{"$ref":"#/components/schemas/Problem"}}}},"404":{"description":"No such class at this venue. Codes: not_found.","content":{"application/problem+json":{"schema":{"$ref":"#/components/schemas/Problem"}}}},"409":{"description":"Another class at this venue already has that name. Codes: conflict.","content":{"application/problem+json":{"schema":{"$ref":"#/components/schemas/Problem"}}}},"429":{"description":"Too many requests. Codes: rate_limited.","content":{"application/problem+json":{"schema":{"$ref":"#/components/schemas/Problem"}}}},"500":{"description":"Something went wrong. Codes: internal_error.","content":{"application/problem+json":{"schema":{"$ref":"#/components/schemas/Problem"}}}}}}},"/v1/organizations/{organizationId}/venues/{venueId}/zones":{"get":{"operationId":"listZones","tags":["floor"],"summary":"List a venue's zones","description":"Every floor, oldest first. Requires `floor:read`.","x-clavian-permission":"floor:read","parameters":[{"schema":{"$ref":"#/components/schemas/OrganizationId"},"required":true,"description":"A TypeID: \"org_\" followed by the base32 form of a UUIDv7.","name":"organizationId","in":"path"},{"schema":{"$ref":"#/components/schemas/VenueId"},"required":true,"description":"A TypeID: \"ven_\" followed by the base32 form of a UUIDv7.","name":"venueId","in":"path"},{"schema":{"type":"string","minLength":1,"description":"The `page.cursor` from the previous response."},"required":false,"description":"The `page.cursor` from the previous response.","name":"cursor","in":"query"},{"schema":{"type":"integer","minimum":1,"maximum":100,"default":25,"description":"Items per page, 1–100."},"required":false,"description":"Items per page, 1–100.","name":"limit","in":"query"}],"responses":{"200":{"description":"A page of zones.","content":{"application/json":{"schema":{"$ref":"#/components/schemas/ZoneList"}}}},"400":{"description":"The request could not be understood. Codes: validation_failed.","content":{"application/problem+json":{"schema":{"$ref":"#/components/schemas/Problem"}}}},"401":{"description":"Not signed in. Codes: unauthenticated.","content":{"application/problem+json":{"schema":{"$ref":"#/components/schemas/Problem"}}}},"403":{"description":"Your role does not allow this. Codes: forbidden, second_factor_required.","content":{"application/problem+json":{"schema":{"$ref":"#/components/schemas/Problem"}}}},"404":{"description":"No such venue in this organisation, or not one you can see. Codes: not_found.","content":{"application/problem+json":{"schema":{"$ref":"#/components/schemas/Problem"}}}},"429":{"description":"Too many requests. Codes: rate_limited.","content":{"application/problem+json":{"schema":{"$ref":"#/components/schemas/Problem"}}}},"500":{"description":"Something went wrong. Codes: internal_error.","content":{"application/problem+json":{"schema":{"$ref":"#/components/schemas/Problem"}}}}}},"post":{"operationId":"createZone","tags":["floor"],"summary":"Create a zone","description":"At most 30 per floor. Requires `floor:arrange`. Audited.","x-clavian-permission":"floor:arrange","parameters":[{"schema":{"$ref":"#/components/schemas/OrganizationId"},"required":true,"description":"A TypeID: \"org_\" followed by the base32 form of a UUIDv7.","name":"organizationId","in":"path"},{"schema":{"$ref":"#/components/schemas/VenueId"},"required":true,"description":"A TypeID: \"ven_\" followed by the base32 form of a UUIDv7.","name":"venueId","in":"path"}],"requestBody":{"required":true,"content":{"application/json":{"schema":{"$ref":"#/components/schemas/CreateZone"}}}},"responses":{"201":{"description":"The zone that was created.","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Zone"}}}},"400":{"description":"The request could not be understood. Codes: validation_failed.","content":{"application/problem+json":{"schema":{"$ref":"#/components/schemas/Problem"}}}},"401":{"description":"Not signed in. Codes: unauthenticated.","content":{"application/problem+json":{"schema":{"$ref":"#/components/schemas/Problem"}}}},"403":{"description":"Your role does not allow this. Codes: forbidden, second_factor_required.","content":{"application/problem+json":{"schema":{"$ref":"#/components/schemas/Problem"}}}},"404":{"description":"No such venue in this organisation, or not one you can see. Codes: not_found.","content":{"application/problem+json":{"schema":{"$ref":"#/components/schemas/Problem"}}}},"409":{"description":"Another zone has that name, or the floor already has 30 zones. Codes: conflict.","content":{"application/problem+json":{"schema":{"$ref":"#/components/schemas/Problem"}}}},"429":{"description":"Too many requests. Codes: rate_limited.","content":{"application/problem+json":{"schema":{"$ref":"#/components/schemas/Problem"}}}},"500":{"description":"Something went wrong. Codes: internal_error.","content":{"application/problem+json":{"schema":{"$ref":"#/components/schemas/Problem"}}}}}}},"/v1/organizations/{organizationId}/venues/{venueId}/zones/{zoneId}":{"patch":{"operationId":"updateZone","tags":["floor"],"summary":"Rename or recolour a zone","description":"Requires `floor:arrange`. Audited with the before and after.","x-clavian-permission":"floor:arrange","parameters":[{"schema":{"$ref":"#/components/schemas/OrganizationId"},"required":true,"description":"A TypeID: \"org_\" followed by the base32 form of a UUIDv7.","name":"organizationId","in":"path"},{"schema":{"$ref":"#/components/schemas/VenueId"},"required":true,"description":"A TypeID: \"ven_\" followed by the base32 form of a UUIDv7.","name":"venueId","in":"path"},{"schema":{"$ref":"#/components/schemas/ZoneId"},"required":true,"description":"A TypeID: \"zon_\" followed by the base32 form of a UUIDv7.","name":"zoneId","in":"path"}],"requestBody":{"required":true,"content":{"application/json":{"schema":{"$ref":"#/components/schemas/UpdateZone"}}}},"responses":{"200":{"description":"The zone as it is now.","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Zone"}}}},"400":{"description":"The request could not be understood. Codes: validation_failed.","content":{"application/problem+json":{"schema":{"$ref":"#/components/schemas/Problem"}}}},"401":{"description":"Not signed in. Codes: unauthenticated.","content":{"application/problem+json":{"schema":{"$ref":"#/components/schemas/Problem"}}}},"403":{"description":"Your role does not allow this. Codes: forbidden, second_factor_required.","content":{"application/problem+json":{"schema":{"$ref":"#/components/schemas/Problem"}}}},"404":{"description":"No such zone at this venue. Codes: not_found.","content":{"application/problem+json":{"schema":{"$ref":"#/components/schemas/Problem"}}}},"409":{"description":"Another zone at this venue already has that name. Codes: conflict.","content":{"application/problem+json":{"schema":{"$ref":"#/components/schemas/Problem"}}}},"429":{"description":"Too many requests. Codes: rate_limited.","content":{"application/problem+json":{"schema":{"$ref":"#/components/schemas/Problem"}}}},"500":{"description":"Something went wrong. Codes: internal_error.","content":{"application/problem+json":{"schema":{"$ref":"#/components/schemas/Problem"}}}}}}},"/v1/organizations/{organizationId}/venues/{venueId}/floor-plan":{"get":{"operationId":"getFloorPlan","tags":["floor"],"summary":"Read one floor’s plan","description":"Returns the plan and its `ETag`; an unarranged floor is an empty plan at version 0. Requires `floor:read`.","x-clavian-permission":"floor:read","parameters":[{"schema":{"$ref":"#/components/schemas/OrganizationId"},"required":true,"description":"A TypeID: \"org_\" followed by the base32 form of a UUIDv7.","name":"organizationId","in":"path"},{"schema":{"$ref":"#/components/schemas/VenueId"},"required":true,"description":"A TypeID: \"ven_\" followed by the base32 form of a UUIDv7.","name":"venueId","in":"path"},{"schema":{"type":["integer","null"],"minimum":0,"maximum":9,"default":0,"description":"The level: 0, the ground floor, unless the venue has more."},"required":false,"description":"The level: 0, the ground floor, unless the venue has more.","name":"floor","in":"query"}],"responses":{"200":{"description":"The plan.","headers":{"ETag":{"schema":{"type":"string","description":"The version to send back as If-Match."},"required":true,"description":"The version to send back as If-Match."}},"content":{"application/json":{"schema":{"$ref":"#/components/schemas/FloorPlan"}}}},"400":{"description":"The request could not be understood. Codes: validation_failed.","content":{"application/problem+json":{"schema":{"$ref":"#/components/schemas/Problem"}}}},"401":{"description":"Not signed in. Codes: unauthenticated.","content":{"application/problem+json":{"schema":{"$ref":"#/components/schemas/Problem"}}}},"403":{"description":"Your role does not allow this. Codes: forbidden, second_factor_required.","content":{"application/problem+json":{"schema":{"$ref":"#/components/schemas/Problem"}}}},"404":{"description":"No such venue in this organisation, or not one you can see. Codes: not_found.","content":{"application/problem+json":{"schema":{"$ref":"#/components/schemas/Problem"}}}},"429":{"description":"Too many requests. Codes: rate_limited.","content":{"application/problem+json":{"schema":{"$ref":"#/components/schemas/Problem"}}}},"500":{"description":"Something went wrong. Codes: internal_error.","content":{"application/problem+json":{"schema":{"$ref":"#/components/schemas/Problem"}}}}}},"put":{"operationId":"putFloorPlan","tags":["floor"],"summary":"Save one floor’s plan","description":"Replaces the plan. Send the `ETag` you read as `If-Match`; a stale one is 412 and nothing is saved. Zones may not overlap, a floor holds at most 30 zones and 100 fixtures, everything stays inside the grid, and each zone appears once, on its own floor — the same rules, and the same messages, as arrange mode. Requires `floor:arrange`. Audited.","x-clavian-permission":"floor:arrange","parameters":[{"schema":{"$ref":"#/components/schemas/OrganizationId"},"required":true,"description":"A TypeID: \"org_\" followed by the base32 form of a UUIDv7.","name":"organizationId","in":"path"},{"schema":{"$ref":"#/components/schemas/VenueId"},"required":true,"description":"A TypeID: \"ven_\" followed by the base32 form of a UUIDv7.","name":"venueId","in":"path"},{"schema":{"type":["integer","null"],"minimum":0,"maximum":9,"default":0,"description":"The level: 0, the ground floor, unless the venue has more."},"required":false,"description":"The level: 0, the ground floor, unless the venue has more.","name":"floor","in":"query"},{"schema":{"type":"string","minLength":1,"maxLength":64,"description":"The ETag this save is based on."},"required":true,"description":"The ETag this save is based on.","name":"if-match","in":"header"}],"requestBody":{"required":true,"content":{"application/json":{"schema":{"$ref":"#/components/schemas/PutFloorPlan"}}}},"responses":{"200":{"description":"The plan as saved.","headers":{"ETag":{"schema":{"type":"string","description":"The version to send back as If-Match."},"required":true,"description":"The version to send back as If-Match."}},"content":{"application/json":{"schema":{"$ref":"#/components/schemas/FloorPlan"}}}},"400":{"description":"The request could not be understood. Codes: validation_failed.","content":{"application/problem+json":{"schema":{"$ref":"#/components/schemas/Problem"}}}},"401":{"description":"Not signed in. Codes: unauthenticated.","content":{"application/problem+json":{"schema":{"$ref":"#/components/schemas/Problem"}}}},"403":{"description":"Your role does not allow this. Codes: forbidden, second_factor_required.","content":{"application/problem+json":{"schema":{"$ref":"#/components/schemas/Problem"}}}},"404":{"description":"No such venue in this organisation, or not one you can see. Codes: not_found.","content":{"application/problem+json":{"schema":{"$ref":"#/components/schemas/Problem"}}}},"412":{"description":"Someone else saved this floor since you read it. Codes: stale_write.","content":{"application/problem+json":{"schema":{"$ref":"#/components/schemas/Problem"}}}},"429":{"description":"Too many requests. Codes: rate_limited.","content":{"application/problem+json":{"schema":{"$ref":"#/components/schemas/Problem"}}}},"500":{"description":"Something went wrong. Codes: internal_error.","content":{"application/problem+json":{"schema":{"$ref":"#/components/schemas/Problem"}}}}}}},"/v1/hq/settings":{"get":{"operationId":"getPlatformSettings","tags":["settings"],"summary":"Read the platform defaults and locks","description":"Every setting in the registry, with its platform value and whether it is locked there (spec/14 §14). Requires `hq:settings`.","x-clavian-permission":"hq:settings","responses":{"200":{"description":"The platform settings screen.","headers":{"ETag":{"schema":{"type":"string","description":"The version to send back as If-Match."},"required":true,"description":"The version to send back as If-Match."}},"content":{"application/json":{"schema":{"$ref":"#/components/schemas/SettingsView"}}}},"401":{"description":"Not signed in. Codes: unauthenticated.","content":{"application/problem+json":{"schema":{"$ref":"#/components/schemas/Problem"}}}},"403":{"description":"Not allowed. Codes: forbidden, second_factor_required.","content":{"application/problem+json":{"schema":{"$ref":"#/components/schemas/Problem"}}}},"429":{"description":"Too many requests. Codes: rate_limited.","content":{"application/problem+json":{"schema":{"$ref":"#/components/schemas/Problem"}}}},"500":{"description":"Something went wrong. Codes: internal_error.","content":{"application/problem+json":{"schema":{"$ref":"#/components/schemas/Problem"}}}}}},"put":{"operationId":"putPlatformSettings","tags":["settings"],"summary":"Change platform defaults and locks","description":"Sets, locks, unlocks or resets platform values, all or none. A lock at platform scope binds every organisation and venue, and is refused (400) on a setting not lockable there (`lockableAt`). Send the `ETag` you read as `If-Match`. Requires `hq:settings`. Each change is audited.","x-clavian-permission":"hq:settings","parameters":[{"schema":{"type":"string","minLength":1,"maxLength":64,"description":"The ETag this write is based on."},"required":true,"description":"The ETag this write is based on.","name":"if-match","in":"header"}],"requestBody":{"required":true,"content":{"application/json":{"schema":{"$ref":"#/components/schemas/PutLockableSettings"}}}},"responses":{"200":{"description":"The platform settings screen as it is now.","headers":{"ETag":{"schema":{"type":"string","description":"The version to send back as If-Match."},"required":true,"description":"The version to send back as If-Match."}},"content":{"application/json":{"schema":{"$ref":"#/components/schemas/SettingsView"}}}},"400":{"description":"The change is not one the registry accepts: an unknown key, a scope it does not take, or a value its schema refuses. Codes: validation_failed.","content":{"application/problem+json":{"schema":{"$ref":"#/components/schemas/Problem"}}}},"401":{"description":"Not signed in. Codes: unauthenticated.","content":{"application/problem+json":{"schema":{"$ref":"#/components/schemas/Problem"}}}},"403":{"description":"Not allowed. Codes: forbidden, second_factor_required.","content":{"application/problem+json":{"schema":{"$ref":"#/components/schemas/Problem"}}}},"412":{"description":"Someone changed these settings since you read them. Codes: stale_write.","content":{"application/problem+json":{"schema":{"$ref":"#/components/schemas/Problem"}}}},"429":{"description":"Too many requests. Codes: rate_limited.","content":{"application/problem+json":{"schema":{"$ref":"#/components/schemas/Problem"}}}},"500":{"description":"Something went wrong. Codes: internal_error.","content":{"application/problem+json":{"schema":{"$ref":"#/components/schemas/Problem"}}}}}}},"/v1/hq/organizations/{organizationId}/settings":{"get":{"operationId":"getHqOrganizationSettings","tags":["settings"],"summary":"Read one organisation’s settings, as HQ","description":"Every setting that can be set or locked for the organisation, with its value and lock. The read is audited in the organisation’s log. Requires `hq:settings`.","x-clavian-permission":"hq:settings","parameters":[{"schema":{"$ref":"#/components/schemas/OrganizationId"},"required":true,"description":"A TypeID: \"org_\" followed by the base32 form of a UUIDv7.","name":"organizationId","in":"path"}],"responses":{"200":{"description":"The organisation’s settings, as HQ sees them.","headers":{"ETag":{"schema":{"type":"string","description":"The version to send back as If-Match."},"required":true,"description":"The version to send back as If-Match."}},"content":{"application/json":{"schema":{"$ref":"#/components/schemas/SettingsView"}}}},"400":{"description":"The request could not be understood. Codes: validation_failed.","content":{"application/problem+json":{"schema":{"$ref":"#/components/schemas/Problem"}}}},"401":{"description":"Not signed in. Codes: unauthenticated.","content":{"application/problem+json":{"schema":{"$ref":"#/components/schemas/Problem"}}}},"403":{"description":"Not allowed. Codes: forbidden, second_factor_required.","content":{"application/problem+json":{"schema":{"$ref":"#/components/schemas/Problem"}}}},"404":{"description":"No such organisation. Codes: not_found.","content":{"application/problem+json":{"schema":{"$ref":"#/components/schemas/Problem"}}}},"429":{"description":"Too many requests. Codes: rate_limited.","content":{"application/problem+json":{"schema":{"$ref":"#/components/schemas/Problem"}}}},"500":{"description":"Something went wrong. Codes: internal_error.","content":{"application/problem+json":{"schema":{"$ref":"#/components/schemas/Problem"}}}}}},"put":{"operationId":"putHqOrganizationSettings","tags":["settings"],"summary":"Set, lock or unlock an organisation’s settings, as HQ","description":"A lock here binds the organisation’s venues, station classes and zones: their editors are refused with `setting-locked`. A lock is refused (400) on a setting not lockable there (`lockableAt`). Refused when the setting is locked at platform scope. Send the `ETag` you read as `If-Match`. Requires `hq:settings`. Each change is one audit row in the organisation’s log.","x-clavian-permission":"hq:settings","parameters":[{"schema":{"$ref":"#/components/schemas/OrganizationId"},"required":true,"description":"A TypeID: \"org_\" followed by the base32 form of a UUIDv7.","name":"organizationId","in":"path"},{"schema":{"type":"string","minLength":1,"maxLength":64,"description":"The ETag this write is based on."},"required":true,"description":"The ETag this write is based on.","name":"if-match","in":"header"}],"requestBody":{"required":true,"content":{"application/json":{"schema":{"$ref":"#/components/schemas/PutLockableSettings"}}}},"responses":{"200":{"description":"The organisation’s settings as they are now.","headers":{"ETag":{"schema":{"type":"string","description":"The version to send back as If-Match."},"required":true,"description":"The version to send back as If-Match."}},"content":{"application/json":{"schema":{"$ref":"#/components/schemas/SettingsView"}}}},"400":{"description":"The change is not one the registry accepts: an unknown key, a scope it does not take, or a value its schema refuses. Codes: validation_failed.","content":{"application/problem+json":{"schema":{"$ref":"#/components/schemas/Problem"}}}},"401":{"description":"Not signed in. Codes: unauthenticated.","content":{"application/problem+json":{"schema":{"$ref":"#/components/schemas/Problem"}}}},"403":{"description":"Your role does not allow this, the setting is not one this screen changes, or it is locked above this scope. Codes: forbidden, second_factor_required, setting_locked.","content":{"application/problem+json":{"schema":{"$ref":"#/components/schemas/Problem"}}}},"404":{"description":"No such organisation. Codes: not_found.","content":{"application/problem+json":{"schema":{"$ref":"#/components/schemas/Problem"}}}},"412":{"description":"Someone changed these settings since you read them. Codes: stale_write.","content":{"application/problem+json":{"schema":{"$ref":"#/components/schemas/Problem"}}}},"429":{"description":"Too many requests. Codes: rate_limited.","content":{"application/problem+json":{"schema":{"$ref":"#/components/schemas/Problem"}}}},"500":{"description":"Something went wrong. Codes: internal_error.","content":{"application/problem+json":{"schema":{"$ref":"#/components/schemas/Problem"}}}}}}},"/v1/organizations/{organizationId}/settings":{"get":{"operationId":"getOrganizationSettings","tags":["settings"],"summary":"Read the organisation’s settings screen","description":"Every setting the organisation’s screen shows, with its value, where it comes from, what it would inherit, whether it is locked and whether you may change it. Requires `settings:read`.","x-clavian-permission":"settings:read","parameters":[{"schema":{"$ref":"#/components/schemas/OrganizationId"},"required":true,"description":"A TypeID: \"org_\" followed by the base32 form of a UUIDv7.","name":"organizationId","in":"path"}],"responses":{"200":{"description":"The organisation’s settings screen.","headers":{"ETag":{"schema":{"type":"string","description":"The version to send back as If-Match."},"required":true,"description":"The version to send back as If-Match."}},"content":{"application/json":{"schema":{"$ref":"#/components/schemas/SettingsView"}}}},"400":{"description":"The request could not be understood. Codes: validation_failed.","content":{"application/problem+json":{"schema":{"$ref":"#/components/schemas/Problem"}}}},"401":{"description":"Not signed in. Codes: unauthenticated.","content":{"application/problem+json":{"schema":{"$ref":"#/components/schemas/Problem"}}}},"403":{"description":"Your role does not allow this. Codes: forbidden, second_factor_required.","content":{"application/problem+json":{"schema":{"$ref":"#/components/schemas/Problem"}}}},"404":{"description":"No such organisation, or not one you can see. Codes: not_found.","content":{"application/problem+json":{"schema":{"$ref":"#/components/schemas/Problem"}}}},"429":{"description":"Too many requests. Codes: rate_limited.","content":{"application/problem+json":{"schema":{"$ref":"#/components/schemas/Problem"}}}},"500":{"description":"Something went wrong. Codes: internal_error.","content":{"application/problem+json":{"schema":{"$ref":"#/components/schemas/Problem"}}}}}},"put":{"operationId":"putOrganizationSettings","tags":["settings"],"summary":"Change the organisation’s settings","description":"Sets or resets values at organisation scope, all or none. Requires `settings:read` and, for each change, the setting’s `editPermission`, and a member whose staff access covers every venue. Send the `ETag` you read as `If-Match`. Each change is audited.","x-clavian-permission":"settings:read","x-clavian-setting-permission":"editPermission","parameters":[{"schema":{"$ref":"#/components/schemas/OrganizationId"},"required":true,"description":"A TypeID: \"org_\" followed by the base32 form of a UUIDv7.","name":"organizationId","in":"path"},{"schema":{"type":"string","minLength":1,"maxLength":64,"description":"The ETag this write is based on."},"required":true,"description":"The ETag this write is based on.","name":"if-match","in":"header"}],"requestBody":{"required":true,"content":{"application/json":{"schema":{"$ref":"#/components/schemas/PutSettings"}}}},"responses":{"200":{"description":"The organisation’s settings screen as it is now.","headers":{"ETag":{"schema":{"type":"string","description":"The version to send back as If-Match."},"required":true,"description":"The version to send back as If-Match."}},"content":{"application/json":{"schema":{"$ref":"#/components/schemas/SettingsView"}}}},"400":{"description":"The change is not one the registry accepts: an unknown key, a scope it does not take, or a value its schema refuses. Codes: validation_failed.","content":{"application/problem+json":{"schema":{"$ref":"#/components/schemas/Problem"}}}},"401":{"description":"Not signed in. Codes: unauthenticated.","content":{"application/problem+json":{"schema":{"$ref":"#/components/schemas/Problem"}}}},"402":{"description":"A setting you changed is not in the organisation’s plan. Codes: entitlement_required.","content":{"application/problem+json":{"schema":{"$ref":"#/components/schemas/Problem"}}}},"403":{"description":"Your role does not allow this, the setting is not one this screen changes, or it is locked above this scope. Codes: forbidden, second_factor_required, setting_locked.","content":{"application/problem+json":{"schema":{"$ref":"#/components/schemas/Problem"}}}},"404":{"description":"No such organisation, or not one you can see. Codes: not_found.","content":{"application/problem+json":{"schema":{"$ref":"#/components/schemas/Problem"}}}},"412":{"description":"Someone changed these settings since you read them. Codes: stale_write.","content":{"application/problem+json":{"schema":{"$ref":"#/components/schemas/Problem"}}}},"429":{"description":"Too many requests. Codes: rate_limited.","content":{"application/problem+json":{"schema":{"$ref":"#/components/schemas/Problem"}}}},"500":{"description":"Something went wrong. Codes: internal_error.","content":{"application/problem+json":{"schema":{"$ref":"#/components/schemas/Problem"}}}}}}},"/v1/organizations/{organizationId}/venues/{venueId}/settings":{"get":{"operationId":"getVenueSettings","tags":["settings"],"summary":"Read a venue’s settings screen","description":"Every setting the venue’s screen shows, with its value, where it comes from, what it would inherit, the values stored for its station classes and zones, whether it is locked and whether you may change it. Requires `settings:read`.","x-clavian-permission":"settings:read","parameters":[{"schema":{"$ref":"#/components/schemas/OrganizationId"},"required":true,"description":"A TypeID: \"org_\" followed by the base32 form of a UUIDv7.","name":"organizationId","in":"path"},{"schema":{"$ref":"#/components/schemas/VenueId"},"required":true,"description":"A TypeID: \"ven_\" followed by the base32 form of a UUIDv7.","name":"venueId","in":"path"}],"responses":{"200":{"description":"The venue’s settings screen.","headers":{"ETag":{"schema":{"type":"string","description":"The version to send back as If-Match."},"required":true,"description":"The version to send back as If-Match."}},"content":{"application/json":{"schema":{"$ref":"#/components/schemas/SettingsView"}}}},"400":{"description":"The request could not be understood. Codes: validation_failed.","content":{"application/problem+json":{"schema":{"$ref":"#/components/schemas/Problem"}}}},"401":{"description":"Not signed in. Codes: unauthenticated.","content":{"application/problem+json":{"schema":{"$ref":"#/components/schemas/Problem"}}}},"403":{"description":"Your role does not allow this. Codes: forbidden, second_factor_required.","content":{"application/problem+json":{"schema":{"$ref":"#/components/schemas/Problem"}}}},"404":{"description":"No such venue in this organisation, or not one you can see. Codes: not_found.","content":{"application/problem+json":{"schema":{"$ref":"#/components/schemas/Problem"}}}},"429":{"description":"Too many requests. Codes: rate_limited.","content":{"application/problem+json":{"schema":{"$ref":"#/components/schemas/Problem"}}}},"500":{"description":"Something went wrong. Codes: internal_error.","content":{"application/problem+json":{"schema":{"$ref":"#/components/schemas/Problem"}}}}}},"put":{"operationId":"putVenueSettings","tags":["settings"],"summary":"Change a venue’s settings","description":"Sets or resets values for the venue, or for one of its station classes or zones, all or none. Requires `settings:read` and, for each change, the setting’s `editPermission`. Refused with `setting-locked` when HQ has locked the setting above the venue. Send the `ETag` you read as `If-Match`. Each change is audited.","x-clavian-permission":"settings:read","x-clavian-setting-permission":"editPermission","parameters":[{"schema":{"$ref":"#/components/schemas/OrganizationId"},"required":true,"description":"A TypeID: \"org_\" followed by the base32 form of a UUIDv7.","name":"organizationId","in":"path"},{"schema":{"$ref":"#/components/schemas/VenueId"},"required":true,"description":"A TypeID: \"ven_\" followed by the base32 form of a UUIDv7.","name":"venueId","in":"path"},{"schema":{"type":"string","minLength":1,"maxLength":64,"description":"The ETag this write is based on."},"required":true,"description":"The ETag this write is based on.","name":"if-match","in":"header"}],"requestBody":{"required":true,"content":{"application/json":{"schema":{"$ref":"#/components/schemas/PutVenueSettings"}}}},"responses":{"200":{"description":"The venue’s settings screen as it is now.","headers":{"ETag":{"schema":{"type":"string","description":"The version to send back as If-Match."},"required":true,"description":"The version to send back as If-Match."}},"content":{"application/json":{"schema":{"$ref":"#/components/schemas/SettingsView"}}}},"400":{"description":"The change is not one the registry accepts: an unknown key, a scope it does not take, or a value its schema refuses. Codes: validation_failed.","content":{"application/problem+json":{"schema":{"$ref":"#/components/schemas/Problem"}}}},"401":{"description":"Not signed in. Codes: unauthenticated.","content":{"application/problem+json":{"schema":{"$ref":"#/components/schemas/Problem"}}}},"402":{"description":"A setting you changed is not in the organisation’s plan. Codes: entitlement_required.","content":{"application/problem+json":{"schema":{"$ref":"#/components/schemas/Problem"}}}},"403":{"description":"Your role does not allow this, the setting is not one this screen changes, or it is locked above this scope. Codes: forbidden, second_factor_required, setting_locked.","content":{"application/problem+json":{"schema":{"$ref":"#/components/schemas/Problem"}}}},"404":{"description":"No such venue in this organisation, or not one you can see. Codes: not_found.","content":{"application/problem+json":{"schema":{"$ref":"#/components/schemas/Problem"}}}},"409":{"description":"The venue is archived. Codes: conflict.","content":{"application/problem+json":{"schema":{"$ref":"#/components/schemas/Problem"}}}},"412":{"description":"Someone changed these settings since you read them. Codes: stale_write.","content":{"application/problem+json":{"schema":{"$ref":"#/components/schemas/Problem"}}}},"429":{"description":"Too many requests. Codes: rate_limited.","content":{"application/problem+json":{"schema":{"$ref":"#/components/schemas/Problem"}}}},"500":{"description":"Something went wrong. Codes: internal_error.","content":{"application/problem+json":{"schema":{"$ref":"#/components/schemas/Problem"}}}}}}},"/v1/me":{"patch":{"operationId":"updateMe","tags":["me"],"summary":"Change your own profile: marketing, and your email address or phone number","description":"Needs a signed-in user: a session cookie in a browser, `Authorization: Bearer` in the apps. It is about the caller only. Marketing is off unless the player switches it on. Switching it off stamps `revokedAt` on the grant in force and stops marketing at once; switching it on again writes a new consent row. Transactional messages are not affected either way. A new `email` or `phoneNumber` changes nothing yet: it needs a session signed in within the last ten minutes, and a code goes to the new value (`202`), which `POST /v1/me/contact-verification` spends to make the change.","requestBody":{"required":true,"content":{"application/json":{"schema":{"$ref":"#/components/schemas/UpdateMe"}}}},"responses":{"200":{"description":"The profile as it now stands.","content":{"application/json":{"schema":{"$ref":"#/components/schemas/MyProfile"}}}},"202":{"description":"A code went to the new email address or phone number.","content":{"application/json":{"schema":{"$ref":"#/components/schemas/ContactVerificationStarted"}}}},"400":{"description":"The request could not be understood, the player has not accepted a privacy policy to opt in under, or the number cannot be used. Codes: validation_failed, consent_required, phone_number_refused.","content":{"application/problem+json":{"schema":{"$ref":"#/components/schemas/Problem"}}}},"401":{"description":"Not signed in. Codes: unauthenticated.","content":{"application/problem+json":{"schema":{"$ref":"#/components/schemas/Problem"}}}},"403":{"description":"Changing an email address or phone number needs a fresh sign-in. Codes: reauthentication_required.","content":{"application/problem+json":{"schema":{"$ref":"#/components/schemas/Problem"}}}},"429":{"description":"Too many requests. Codes: rate_limited.","content":{"application/problem+json":{"schema":{"$ref":"#/components/schemas/Problem"}}}},"500":{"description":"Something went wrong. Codes: internal_error.","content":{"application/problem+json":{"schema":{"$ref":"#/components/schemas/Problem"}}}},"503":{"description":"The code could not be sent; nothing changed. Codes: otp_delivery_unavailable.","content":{"application/problem+json":{"schema":{"$ref":"#/components/schemas/Problem"}}}}}}},"/v1/me/consents":{"get":{"operationId":"getMyConsents","tags":["me"],"summary":"What you have agreed to","description":"Needs a signed-in user: a session cookie in a browser, `Authorization: Bearer` in the apps. It is about the caller only. The terms and privacy versions accepted, the marketing consent in force, and every consent row ever written for the player — the record a data export carries.","responses":{"200":{"description":"The player's consents.","content":{"application/json":{"schema":{"$ref":"#/components/schemas/MyConsents"}}}},"401":{"description":"Not signed in. Codes: unauthenticated.","content":{"application/problem+json":{"schema":{"$ref":"#/components/schemas/Problem"}}}},"429":{"description":"Too many requests. Codes: rate_limited.","content":{"application/problem+json":{"schema":{"$ref":"#/components/schemas/Problem"}}}},"500":{"description":"Something went wrong. Codes: internal_error.","content":{"application/problem+json":{"schema":{"$ref":"#/components/schemas/Problem"}}}}}},"post":{"operationId":"acceptConsents","tags":["me"],"summary":"Accept a new version of the terms or the privacy policy","description":"Needs a signed-in user: a session cookie in a browser, `Authorization: Bearer` in the apps. It is about the caller only. Each version accepted is a new row; the row for the version accepted before is kept unchanged. Accepting a version already accepted changes nothing. Where the brand configuration names the documents in force, a version that is not the one in force is refused.","requestBody":{"required":true,"content":{"application/json":{"schema":{"$ref":"#/components/schemas/AcceptConsents"}}}},"responses":{"200":{"description":"The player's consents, with the new acceptance.","content":{"application/json":{"schema":{"$ref":"#/components/schemas/MyConsents"}}}},"400":{"description":"The request could not be understood. Codes: validation_failed.","content":{"application/problem+json":{"schema":{"$ref":"#/components/schemas/Problem"}}}},"401":{"description":"Not signed in. Codes: unauthenticated.","content":{"application/problem+json":{"schema":{"$ref":"#/components/schemas/Problem"}}}},"409":{"description":"The version sent is not the one in force; show the current document. Codes: consent_version_outdated.","content":{"application/problem+json":{"schema":{"$ref":"#/components/schemas/Problem"}}}},"429":{"description":"Too many requests. Codes: rate_limited.","content":{"application/problem+json":{"schema":{"$ref":"#/components/schemas/Problem"}}}},"500":{"description":"Something went wrong. Codes: internal_error.","content":{"application/problem+json":{"schema":{"$ref":"#/components/schemas/Problem"}}}}}}},"/v1/notifications/prefs":{"get":{"operationId":"getNotificationPrefs","tags":["notifications"],"summary":"Your notification preferences","description":"Needs a signed-in user. It is about the caller only. Today: whether marketing is on, and since when.","responses":{"200":{"description":"The preferences.","content":{"application/json":{"schema":{"$ref":"#/components/schemas/NotificationPrefs"}}}},"401":{"description":"Not signed in. Codes: unauthenticated.","content":{"application/problem+json":{"schema":{"$ref":"#/components/schemas/Problem"}}}},"429":{"description":"Too many requests. Codes: rate_limited.","content":{"application/problem+json":{"schema":{"$ref":"#/components/schemas/Problem"}}}},"500":{"description":"Something went wrong. Codes: internal_error.","content":{"application/problem+json":{"schema":{"$ref":"#/components/schemas/Problem"}}}}}},"put":{"operationId":"putNotificationPrefs","tags":["notifications"],"summary":"Change your notification preferences","description":"Needs a signed-in user. It is about the caller only. Switching marketing off stamps `revokedAt` on the grant in force and stops marketing at once; switching it on writes a new consent row. Transactional messages are not affected.","requestBody":{"required":true,"content":{"application/json":{"schema":{"$ref":"#/components/schemas/PutNotificationPrefs"}}}},"responses":{"200":{"description":"The preferences as they now stand.","content":{"application/json":{"schema":{"$ref":"#/components/schemas/NotificationPrefs"}}}},"400":{"description":"The request could not be understood, or the player has not accepted a privacy policy to opt in under. Codes: validation_failed, consent_required.","content":{"application/problem+json":{"schema":{"$ref":"#/components/schemas/Problem"}}}},"401":{"description":"Not signed in. Codes: unauthenticated.","content":{"application/problem+json":{"schema":{"$ref":"#/components/schemas/Problem"}}}},"429":{"description":"Too many requests. Codes: rate_limited.","content":{"application/problem+json":{"schema":{"$ref":"#/components/schemas/Problem"}}}},"500":{"description":"Something went wrong. Codes: internal_error.","content":{"application/problem+json":{"schema":{"$ref":"#/components/schemas/Problem"}}}}}}},"/v1/organizations/{organizationId}/members":{"get":{"operationId":"listMembers","tags":["members"],"summary":"The organisation’s staff","description":"Every member, oldest first, with their role, venues and whether they have a second factor.","x-clavian-permission":"members:read","parameters":[{"schema":{"$ref":"#/components/schemas/OrganizationId"},"required":true,"description":"A TypeID: \"org_\" followed by the base32 form of a UUIDv7.","name":"organizationId","in":"path"},{"schema":{"type":"string","minLength":1,"description":"The `page.cursor` from the previous response."},"required":false,"description":"The `page.cursor` from the previous response.","name":"cursor","in":"query"},{"schema":{"type":"integer","minimum":1,"maximum":100,"default":25,"description":"Items per page, 1–100."},"required":false,"description":"Items per page, 1–100.","name":"limit","in":"query"}],"responses":{"200":{"description":"A page of members.","content":{"application/json":{"schema":{"$ref":"#/components/schemas/MemberList"}}}},"400":{"description":"The request could not be understood. Codes: validation_failed.","content":{"application/problem+json":{"schema":{"$ref":"#/components/schemas/Problem"}}}},"401":{"description":"Not signed in. Codes: unauthenticated.","content":{"application/problem+json":{"schema":{"$ref":"#/components/schemas/Problem"}}}},"403":{"description":"Not allowed. Codes: forbidden, second_factor_required.","content":{"application/problem+json":{"schema":{"$ref":"#/components/schemas/Problem"}}}},"404":{"description":"No such organisation or member, or not one you can see. Codes: not_found.","content":{"application/problem+json":{"schema":{"$ref":"#/components/schemas/Problem"}}}},"429":{"description":"Too many requests. Codes: rate_limited.","content":{"application/problem+json":{"schema":{"$ref":"#/components/schemas/Problem"}}}},"500":{"description":"Something went wrong. Codes: internal_error.","content":{"application/problem+json":{"schema":{"$ref":"#/components/schemas/Problem"}}}}}}},"/v1/organizations/{organizationId}/members/{memberId}/role":{"put":{"operationId":"changeMemberRole","tags":["members"],"summary":"Give a member a different role","description":"The role must be below your own — its permissions a strict subset of yours — and only an owner may grant, change or remove the owner role (spec/07 §6.3). The last owner cannot be given another role.","x-clavian-permission":"members:roles","parameters":[{"schema":{"$ref":"#/components/schemas/OrganizationId"},"required":true,"description":"A TypeID: \"org_\" followed by the base32 form of a UUIDv7.","name":"organizationId","in":"path"},{"schema":{"$ref":"#/components/schemas/MemberId"},"required":true,"description":"A TypeID: \"mem_\" followed by the base32 form of a UUIDv7.","name":"memberId","in":"path"}],"requestBody":{"required":true,"content":{"application/json":{"schema":{"$ref":"#/components/schemas/ChangeMemberRole"}}}},"responses":{"200":{"description":"The member, with the new role.","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Member"}}}},"400":{"description":"The request could not be understood. Codes: validation_failed.","content":{"application/problem+json":{"schema":{"$ref":"#/components/schemas/Problem"}}}},"401":{"description":"Not signed in. Codes: unauthenticated.","content":{"application/problem+json":{"schema":{"$ref":"#/components/schemas/Problem"}}}},"403":{"description":"Not allowed. Codes: forbidden, second_factor_required.","content":{"application/problem+json":{"schema":{"$ref":"#/components/schemas/Problem"}}}},"404":{"description":"No such organisation or member, or not one you can see. Codes: not_found.","content":{"application/problem+json":{"schema":{"$ref":"#/components/schemas/Problem"}}}},"409":{"description":"That would leave the organisation without an owner. Codes: last_owner.","content":{"application/problem+json":{"schema":{"$ref":"#/components/schemas/Problem"}}}},"429":{"description":"Too many requests. Codes: rate_limited.","content":{"application/problem+json":{"schema":{"$ref":"#/components/schemas/Problem"}}}},"500":{"description":"Something went wrong. Codes: internal_error.","content":{"application/problem+json":{"schema":{"$ref":"#/components/schemas/Problem"}}}}}}},"/v1/organizations/{organizationId}/members/{memberId}/venues":{"put":{"operationId":"changeMemberVenues","tags":["members"],"summary":"Change which venues a member works at","description":"An empty list is every venue. The role must be below your own — its permissions a strict subset of yours — and only an owner may grant, change or remove the owner role (spec/07 §6.3). An owner works at every venue.","x-clavian-permission":"members:update","parameters":[{"schema":{"$ref":"#/components/schemas/OrganizationId"},"required":true,"description":"A TypeID: \"org_\" followed by the base32 form of a UUIDv7.","name":"organizationId","in":"path"},{"schema":{"$ref":"#/components/schemas/MemberId"},"required":true,"description":"A TypeID: \"mem_\" followed by the base32 form of a UUIDv7.","name":"memberId","in":"path"}],"requestBody":{"required":true,"content":{"application/json":{"schema":{"$ref":"#/components/schemas/ChangeMemberVenues"}}}},"responses":{"200":{"description":"The member, with the new venues.","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Member"}}}},"400":{"description":"The request could not be understood. Codes: validation_failed.","content":{"application/problem+json":{"schema":{"$ref":"#/components/schemas/Problem"}}}},"401":{"description":"Not signed in. Codes: unauthenticated.","content":{"application/problem+json":{"schema":{"$ref":"#/components/schemas/Problem"}}}},"403":{"description":"Not allowed. Codes: forbidden, second_factor_required.","content":{"application/problem+json":{"schema":{"$ref":"#/components/schemas/Problem"}}}},"404":{"description":"No such organisation or member, or not one you can see. Codes: not_found.","content":{"application/problem+json":{"schema":{"$ref":"#/components/schemas/Problem"}}}},"429":{"description":"Too many requests. Codes: rate_limited.","content":{"application/problem+json":{"schema":{"$ref":"#/components/schemas/Problem"}}}},"500":{"description":"Something went wrong. Codes: internal_error.","content":{"application/problem+json":{"schema":{"$ref":"#/components/schemas/Problem"}}}}}}},"/v1/organizations/{organizationId}/members/{memberId}":{"delete":{"operationId":"removeMember","tags":["members"],"summary":"Take someone off the staff","description":"The role must be below your own — its permissions a strict subset of yours — and only an owner may grant, change or remove the owner role (spec/07 §6.3). Their console access ends on their next request; their account, and signing in as a player, are untouched. The last owner cannot be removed.","x-clavian-permission":"members:remove","parameters":[{"schema":{"$ref":"#/components/schemas/OrganizationId"},"required":true,"description":"A TypeID: \"org_\" followed by the base32 form of a UUIDv7.","name":"organizationId","in":"path"},{"schema":{"$ref":"#/components/schemas/MemberId"},"required":true,"description":"A TypeID: \"mem_\" followed by the base32 form of a UUIDv7.","name":"memberId","in":"path"}],"responses":{"204":{"description":"Removed."},"400":{"description":"The request could not be understood. Codes: validation_failed.","content":{"application/problem+json":{"schema":{"$ref":"#/components/schemas/Problem"}}}},"401":{"description":"Not signed in. Codes: unauthenticated.","content":{"application/problem+json":{"schema":{"$ref":"#/components/schemas/Problem"}}}},"403":{"description":"Not allowed. Codes: forbidden, second_factor_required.","content":{"application/problem+json":{"schema":{"$ref":"#/components/schemas/Problem"}}}},"404":{"description":"No such organisation or member, or not one you can see. Codes: not_found.","content":{"application/problem+json":{"schema":{"$ref":"#/components/schemas/Problem"}}}},"409":{"description":"That would leave the organisation without an owner. Codes: last_owner.","content":{"application/problem+json":{"schema":{"$ref":"#/components/schemas/Problem"}}}},"429":{"description":"Too many requests. Codes: rate_limited.","content":{"application/problem+json":{"schema":{"$ref":"#/components/schemas/Problem"}}}},"500":{"description":"Something went wrong. Codes: internal_error.","content":{"application/problem+json":{"schema":{"$ref":"#/components/schemas/Problem"}}}}}},"get":{"operationId":"getMember","tags":["members"],"summary":"One member of the staff","description":"Their role, venues, status and whether they have a second factor. A member who works at some venues only sees the members who work at those venues, and no one else.","x-clavian-permission":"members:read","parameters":[{"schema":{"$ref":"#/components/schemas/OrganizationId"},"required":true,"description":"A TypeID: \"org_\" followed by the base32 form of a UUIDv7.","name":"organizationId","in":"path"},{"schema":{"$ref":"#/components/schemas/MemberId"},"required":true,"description":"A TypeID: \"mem_\" followed by the base32 form of a UUIDv7.","name":"memberId","in":"path"}],"responses":{"200":{"description":"The member.","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Member"}}}},"400":{"description":"The request could not be understood. Codes: validation_failed.","content":{"application/problem+json":{"schema":{"$ref":"#/components/schemas/Problem"}}}},"401":{"description":"Not signed in. Codes: unauthenticated.","content":{"application/problem+json":{"schema":{"$ref":"#/components/schemas/Problem"}}}},"403":{"description":"Not allowed. Codes: forbidden, second_factor_required.","content":{"application/problem+json":{"schema":{"$ref":"#/components/schemas/Problem"}}}},"404":{"description":"No such organisation or member, or not one you can see. Codes: not_found.","content":{"application/problem+json":{"schema":{"$ref":"#/components/schemas/Problem"}}}},"429":{"description":"Too many requests. Codes: rate_limited.","content":{"application/problem+json":{"schema":{"$ref":"#/components/schemas/Problem"}}}},"500":{"description":"Something went wrong. Codes: internal_error.","content":{"application/problem+json":{"schema":{"$ref":"#/components/schemas/Problem"}}}}}}},"/v1/organizations/{organizationId}/invitations":{"post":{"operationId":"createInvitation","tags":["members"],"summary":"Invite someone to the staff, by phone or by email","description":"The role must be below your own — its permissions a strict subset of yours — and only an owner may grant, change or remove the owner role (spec/07 §6.3). The link works once, for seven days, and only for someone signed in with that number or address. The message (N-25) is sent when a provider can carry it, and `delivery` says whether it was.","x-clavian-permission":"members:invite","parameters":[{"schema":{"$ref":"#/components/schemas/OrganizationId"},"required":true,"description":"A TypeID: \"org_\" followed by the base32 form of a UUIDv7.","name":"organizationId","in":"path"}],"requestBody":{"required":true,"content":{"application/json":{"schema":{"$ref":"#/components/schemas/CreateInvitation"}}}},"responses":{"201":{"description":"The invitation, and whether its message went.","content":{"application/json":{"schema":{"$ref":"#/components/schemas/CreatedInvitation"}}}},"400":{"description":"The request could not be understood. Codes: validation_failed.","content":{"application/problem+json":{"schema":{"$ref":"#/components/schemas/Problem"}}}},"401":{"description":"Not signed in. Codes: unauthenticated.","content":{"application/problem+json":{"schema":{"$ref":"#/components/schemas/Problem"}}}},"403":{"description":"Not allowed. Codes: forbidden, second_factor_required.","content":{"application/problem+json":{"schema":{"$ref":"#/components/schemas/Problem"}}}},"404":{"description":"No such organisation or member, or not one you can see. Codes: not_found.","content":{"application/problem+json":{"schema":{"$ref":"#/components/schemas/Problem"}}}},"409":{"description":"That person is already on the staff. Codes: already_a_member.","content":{"application/problem+json":{"schema":{"$ref":"#/components/schemas/Problem"}}}},"429":{"description":"Too many requests. Codes: rate_limited.","content":{"application/problem+json":{"schema":{"$ref":"#/components/schemas/Problem"}}}},"500":{"description":"Something went wrong. Codes: internal_error.","content":{"application/problem+json":{"schema":{"$ref":"#/components/schemas/Problem"}}}}}},"get":{"operationId":"listInvitations","tags":["members"],"summary":"Invitations sent, newest first","description":"Pending, accepted, revoked and expired. Never the link itself.","x-clavian-permission":"members:read","parameters":[{"schema":{"$ref":"#/components/schemas/OrganizationId"},"required":true,"description":"A TypeID: \"org_\" followed by the base32 form of a UUIDv7.","name":"organizationId","in":"path"},{"schema":{"type":"string","minLength":1,"description":"The `page.cursor` from the previous response."},"required":false,"description":"The `page.cursor` from the previous response.","name":"cursor","in":"query"},{"schema":{"type":"integer","minimum":1,"maximum":100,"default":25,"description":"Items per page, 1–100."},"required":false,"description":"Items per page, 1–100.","name":"limit","in":"query"}],"responses":{"200":{"description":"A page of invitations.","content":{"application/json":{"schema":{"$ref":"#/components/schemas/InvitationList"}}}},"400":{"description":"The request could not be understood. Codes: validation_failed.","content":{"application/problem+json":{"schema":{"$ref":"#/components/schemas/Problem"}}}},"401":{"description":"Not signed in. Codes: unauthenticated.","content":{"application/problem+json":{"schema":{"$ref":"#/components/schemas/Problem"}}}},"403":{"description":"Not allowed. Codes: forbidden, second_factor_required.","content":{"application/problem+json":{"schema":{"$ref":"#/components/schemas/Problem"}}}},"404":{"description":"No such organisation or member, or not one you can see. Codes: not_found.","content":{"application/problem+json":{"schema":{"$ref":"#/components/schemas/Problem"}}}},"429":{"description":"Too many requests. Codes: rate_limited.","content":{"application/problem+json":{"schema":{"$ref":"#/components/schemas/Problem"}}}},"500":{"description":"Something went wrong. Codes: internal_error.","content":{"application/problem+json":{"schema":{"$ref":"#/components/schemas/Problem"}}}}}}},"/v1/organizations/{organizationId}/invitations/{invitationId}":{"delete":{"operationId":"revokeInvitation","tags":["members"],"summary":"Withdraw an invitation","description":"The link stops working at once. The role must be below your own — its permissions a strict subset of yours — and only an owner may grant, change or remove the owner role (spec/07 §6.3).","x-clavian-permission":"members:invite","parameters":[{"schema":{"$ref":"#/components/schemas/OrganizationId"},"required":true,"description":"A TypeID: \"org_\" followed by the base32 form of a UUIDv7.","name":"organizationId","in":"path"},{"schema":{"$ref":"#/components/schemas/InvitationId"},"required":true,"description":"A TypeID: \"inv_\" followed by the base32 form of a UUIDv7.","name":"invitationId","in":"path"}],"responses":{"204":{"description":"Withdrawn."},"400":{"description":"The request could not be understood. Codes: validation_failed.","content":{"application/problem+json":{"schema":{"$ref":"#/components/schemas/Problem"}}}},"401":{"description":"Not signed in. Codes: unauthenticated.","content":{"application/problem+json":{"schema":{"$ref":"#/components/schemas/Problem"}}}},"403":{"description":"Not allowed. Codes: forbidden, second_factor_required.","content":{"application/problem+json":{"schema":{"$ref":"#/components/schemas/Problem"}}}},"404":{"description":"No such invitation in this organisation. Codes: not_found.","content":{"application/problem+json":{"schema":{"$ref":"#/components/schemas/Problem"}}}},"409":{"description":"The invitation was already used, withdrawn or expired. Codes: invitation_unavailable.","content":{"application/problem+json":{"schema":{"$ref":"#/components/schemas/Problem"}}}},"429":{"description":"Too many requests. Codes: rate_limited.","content":{"application/problem+json":{"schema":{"$ref":"#/components/schemas/Problem"}}}},"500":{"description":"Something went wrong. Codes: internal_error.","content":{"application/problem+json":{"schema":{"$ref":"#/components/schemas/Problem"}}}}}}},"/v1/invitations/accept":{"post":{"operationId":"acceptInvitation","tags":["members"],"summary":"Join the staff with an invitation link","description":"Needs a session signed in with the invited phone number or email address, verified. When the role needs a second factor (`staff.require2faRoles`), the session must have completed one — set up an authenticator first. Adds this one membership and no other.","requestBody":{"required":true,"content":{"application/json":{"schema":{"$ref":"#/components/schemas/AcceptInvitation"}}}},"responses":{"200":{"description":"The new membership.","content":{"application/json":{"schema":{"$ref":"#/components/schemas/AcceptedInvitation"}}}},"400":{"description":"The request could not be understood. Codes: validation_failed.","content":{"application/problem+json":{"schema":{"$ref":"#/components/schemas/Problem"}}}},"401":{"description":"Not signed in. Codes: unauthenticated.","content":{"application/problem+json":{"schema":{"$ref":"#/components/schemas/Problem"}}}},"403":{"description":"Signed in as someone other than the person invited, or the role needs a second factor first. Codes: invitation_contact_mismatch, second_factor_required.","content":{"application/problem+json":{"schema":{"$ref":"#/components/schemas/Problem"}}}},"404":{"description":"No such invitation. Codes: not_found.","content":{"application/problem+json":{"schema":{"$ref":"#/components/schemas/Problem"}}}},"409":{"description":"The invitation was used, withdrawn or has expired, or you are already on the staff. Codes: invitation_unavailable, already_a_member.","content":{"application/problem+json":{"schema":{"$ref":"#/components/schemas/Problem"}}}},"429":{"description":"Too many requests. Codes: rate_limited.","content":{"application/problem+json":{"schema":{"$ref":"#/components/schemas/Problem"}}}},"500":{"description":"Something went wrong. Codes: internal_error.","content":{"application/problem+json":{"schema":{"$ref":"#/components/schemas/Problem"}}}}}}},"/v1/organizations/{organizationId}/venues/{venueId}/players/{playerId}/ban":{"post":{"operationId":"banPlayerAtVenue","tags":["players"],"summary":"Ban a player from one venue","description":"The player can no longer start a session or order at this venue; every other venue is untouched. Needs `players:ban` and a venue scope that includes this venue. A ban already on record at this scope — in force or lapsed — is changed to this reason and expiry (`200`) rather than joined by a second (`201`). One audit row either way.","x-clavian-permission":"players:ban","parameters":[{"schema":{"$ref":"#/components/schemas/OrganizationId"},"required":true,"description":"A TypeID: \"org_\" followed by the base32 form of a UUIDv7.","name":"organizationId","in":"path"},{"schema":{"$ref":"#/components/schemas/VenueId"},"required":true,"description":"A TypeID: \"ven_\" followed by the base32 form of a UUIDv7.","name":"venueId","in":"path"},{"schema":{"$ref":"#/components/schemas/PlayerId"},"required":true,"description":"A TypeID: \"ply_\" followed by the base32 form of a UUIDv7.","name":"playerId","in":"path"}],"requestBody":{"required":true,"content":{"application/json":{"schema":{"$ref":"#/components/schemas/BanPlayer"}}}},"responses":{"200":{"description":"The ban on record here, changed.","content":{"application/json":{"schema":{"$ref":"#/components/schemas/PlayerBan"}}}},"201":{"description":"The new ban.","content":{"application/json":{"schema":{"$ref":"#/components/schemas/PlayerBan"}}}},"400":{"description":"The request could not be understood. Codes: validation_failed.","content":{"application/problem+json":{"schema":{"$ref":"#/components/schemas/Problem"}}}},"401":{"description":"Not signed in. Codes: unauthenticated.","content":{"application/problem+json":{"schema":{"$ref":"#/components/schemas/Problem"}}}},"403":{"description":"Not allowed. Codes: forbidden, second_factor_required.","content":{"application/problem+json":{"schema":{"$ref":"#/components/schemas/Problem"}}}},"404":{"description":"No such organisation, venue or player, or not one you can see. Codes: not_found.","content":{"application/problem+json":{"schema":{"$ref":"#/components/schemas/Problem"}}}},"409":{"description":"Someone changed this player’s ban at the same moment; send it again. Codes: conflict.","content":{"application/problem+json":{"schema":{"$ref":"#/components/schemas/Problem"}}}},"429":{"description":"Too many requests. Codes: rate_limited.","content":{"application/problem+json":{"schema":{"$ref":"#/components/schemas/Problem"}}}},"500":{"description":"Something went wrong. Codes: internal_error.","content":{"application/problem+json":{"schema":{"$ref":"#/components/schemas/Problem"}}}}}}},"/v1/organizations/{organizationId}/venues/{venueId}/players/{playerId}/unban":{"post":{"operationId":"unbanPlayerAtVenue","tags":["players"],"summary":"Lift a player’s ban at one venue","description":"Lifts the ban on record at this venue, with a reason; it stays on record as lifted. An organisation ban, if there is one, still applies here. Needs `players:ban` and a venue scope that includes this venue.","x-clavian-permission":"players:ban","parameters":[{"schema":{"$ref":"#/components/schemas/OrganizationId"},"required":true,"description":"A TypeID: \"org_\" followed by the base32 form of a UUIDv7.","name":"organizationId","in":"path"},{"schema":{"$ref":"#/components/schemas/VenueId"},"required":true,"description":"A TypeID: \"ven_\" followed by the base32 form of a UUIDv7.","name":"venueId","in":"path"},{"schema":{"$ref":"#/components/schemas/PlayerId"},"required":true,"description":"A TypeID: \"ply_\" followed by the base32 form of a UUIDv7.","name":"playerId","in":"path"}],"requestBody":{"required":true,"content":{"application/json":{"schema":{"$ref":"#/components/schemas/LiftBan"}}}},"responses":{"200":{"description":"The ban, lifted.","content":{"application/json":{"schema":{"$ref":"#/components/schemas/PlayerBan"}}}},"400":{"description":"The request could not be understood. Codes: validation_failed.","content":{"application/problem+json":{"schema":{"$ref":"#/components/schemas/Problem"}}}},"401":{"description":"Not signed in. Codes: unauthenticated.","content":{"application/problem+json":{"schema":{"$ref":"#/components/schemas/Problem"}}}},"403":{"description":"Not allowed. Codes: forbidden, second_factor_required.","content":{"application/problem+json":{"schema":{"$ref":"#/components/schemas/Problem"}}}},"404":{"description":"No such organisation, venue or player, or no ban on record for them at this scope. Codes: not_found.","content":{"application/problem+json":{"schema":{"$ref":"#/components/schemas/Problem"}}}},"409":{"description":"Someone changed this player’s ban at the same moment; send it again. Codes: conflict.","content":{"application/problem+json":{"schema":{"$ref":"#/components/schemas/Problem"}}}},"429":{"description":"Too many requests. Codes: rate_limited.","content":{"application/problem+json":{"schema":{"$ref":"#/components/schemas/Problem"}}}},"500":{"description":"Something went wrong. Codes: internal_error.","content":{"application/problem+json":{"schema":{"$ref":"#/components/schemas/Problem"}}}}}}},"/v1/organizations/{organizationId}/players/{playerId}/ban":{"post":{"operationId":"banPlayer","tags":["players"],"summary":"Ban a player from every venue of the organisation","description":"The player can no longer start a session or order at any venue of this organisation, including venues opened later. Needs `players:ban` and a member who works at every venue — an owner, or a member with no venue scope. Names no venue: `X-Clavian-Venue` is refused here; to ban at one venue, use `…/venues/{venueId}/players/{playerId}/ban`. A ban already on record at this scope — in force or lapsed — is changed to this reason and expiry (`200`) rather than joined by a second (`201`). One audit row either way.","x-clavian-permission":"players:ban","parameters":[{"schema":{"$ref":"#/components/schemas/OrganizationId"},"required":true,"description":"A TypeID: \"org_\" followed by the base32 form of a UUIDv7.","name":"organizationId","in":"path"},{"schema":{"$ref":"#/components/schemas/PlayerId"},"required":true,"description":"A TypeID: \"ply_\" followed by the base32 form of a UUIDv7.","name":"playerId","in":"path"}],"requestBody":{"required":true,"content":{"application/json":{"schema":{"$ref":"#/components/schemas/BanPlayer"}}}},"responses":{"200":{"description":"The organisation ban on record, changed.","content":{"application/json":{"schema":{"$ref":"#/components/schemas/PlayerBan"}}}},"201":{"description":"The new organisation ban.","content":{"application/json":{"schema":{"$ref":"#/components/schemas/PlayerBan"}}}},"400":{"description":"The request could not be understood. Codes: validation_failed.","content":{"application/problem+json":{"schema":{"$ref":"#/components/schemas/Problem"}}}},"401":{"description":"Not signed in. Codes: unauthenticated.","content":{"application/problem+json":{"schema":{"$ref":"#/components/schemas/Problem"}}}},"403":{"description":"Not allowed. Codes: forbidden, second_factor_required.","content":{"application/problem+json":{"schema":{"$ref":"#/components/schemas/Problem"}}}},"404":{"description":"No such organisation, venue or player, or not one you can see. Codes: not_found.","content":{"application/problem+json":{"schema":{"$ref":"#/components/schemas/Problem"}}}},"409":{"description":"Someone changed this player’s ban at the same moment; send it again. Codes: conflict.","content":{"application/problem+json":{"schema":{"$ref":"#/components/schemas/Problem"}}}},"429":{"description":"Too many requests. Codes: rate_limited.","content":{"application/problem+json":{"schema":{"$ref":"#/components/schemas/Problem"}}}},"500":{"description":"Something went wrong. Codes: internal_error.","content":{"application/problem+json":{"schema":{"$ref":"#/components/schemas/Problem"}}}}}}},"/v1/organizations/{organizationId}/players/{playerId}/unban":{"post":{"operationId":"unbanPlayer","tags":["players"],"summary":"Lift a player’s organisation ban","description":"Lifts the organisation ban on record, with a reason; it stays on record as lifted. A ban at one venue, if there is one, still applies there. Needs `players:ban` and a member who works at every venue.","x-clavian-permission":"players:ban","parameters":[{"schema":{"$ref":"#/components/schemas/OrganizationId"},"required":true,"description":"A TypeID: \"org_\" followed by the base32 form of a UUIDv7.","name":"organizationId","in":"path"},{"schema":{"$ref":"#/components/schemas/PlayerId"},"required":true,"description":"A TypeID: \"ply_\" followed by the base32 form of a UUIDv7.","name":"playerId","in":"path"}],"requestBody":{"required":true,"content":{"application/json":{"schema":{"$ref":"#/components/schemas/LiftBan"}}}},"responses":{"200":{"description":"The organisation ban, lifted.","content":{"application/json":{"schema":{"$ref":"#/components/schemas/PlayerBan"}}}},"400":{"description":"The request could not be understood. Codes: validation_failed.","content":{"application/problem+json":{"schema":{"$ref":"#/components/schemas/Problem"}}}},"401":{"description":"Not signed in. Codes: unauthenticated.","content":{"application/problem+json":{"schema":{"$ref":"#/components/schemas/Problem"}}}},"403":{"description":"Not allowed. Codes: forbidden, second_factor_required.","content":{"application/problem+json":{"schema":{"$ref":"#/components/schemas/Problem"}}}},"404":{"description":"No such organisation, venue or player, or no ban on record for them at this scope. Codes: not_found.","content":{"application/problem+json":{"schema":{"$ref":"#/components/schemas/Problem"}}}},"409":{"description":"Someone changed this player’s ban at the same moment; send it again. Codes: conflict.","content":{"application/problem+json":{"schema":{"$ref":"#/components/schemas/Problem"}}}},"429":{"description":"Too many requests. Codes: rate_limited.","content":{"application/problem+json":{"schema":{"$ref":"#/components/schemas/Problem"}}}},"500":{"description":"Something went wrong. Codes: internal_error.","content":{"application/problem+json":{"schema":{"$ref":"#/components/schemas/Problem"}}}}}}},"/v1/organizations/{organizationId}/players/{playerId}/bans":{"get":{"operationId":"listPlayerBans","tags":["players"],"summary":"A player’s bans in this organisation, reasons included","description":"Newest first: in force, lapsed and lifted, at every venue the member works at and across the organisation. This organisation’s bans only — never another’s, and never a platform ban. Needs `players:ban`.","x-clavian-permission":"players:ban","parameters":[{"schema":{"$ref":"#/components/schemas/OrganizationId"},"required":true,"description":"A TypeID: \"org_\" followed by the base32 form of a UUIDv7.","name":"organizationId","in":"path"},{"schema":{"$ref":"#/components/schemas/PlayerId"},"required":true,"description":"A TypeID: \"ply_\" followed by the base32 form of a UUIDv7.","name":"playerId","in":"path"},{"schema":{"type":"string","minLength":1,"description":"The `page.cursor` from the previous response."},"required":false,"description":"The `page.cursor` from the previous response.","name":"cursor","in":"query"},{"schema":{"type":"integer","minimum":1,"maximum":100,"default":25,"description":"Items per page, 1–100."},"required":false,"description":"Items per page, 1–100.","name":"limit","in":"query"}],"responses":{"200":{"description":"A page of bans.","content":{"application/json":{"schema":{"$ref":"#/components/schemas/PlayerBanList"}}}},"400":{"description":"The request could not be understood. Codes: validation_failed.","content":{"application/problem+json":{"schema":{"$ref":"#/components/schemas/Problem"}}}},"401":{"description":"Not signed in. Codes: unauthenticated.","content":{"application/problem+json":{"schema":{"$ref":"#/components/schemas/Problem"}}}},"403":{"description":"Not allowed. Codes: forbidden, second_factor_required.","content":{"application/problem+json":{"schema":{"$ref":"#/components/schemas/Problem"}}}},"404":{"description":"No such organisation, venue or player, or not one you can see. Codes: not_found.","content":{"application/problem+json":{"schema":{"$ref":"#/components/schemas/Problem"}}}},"429":{"description":"Too many requests. Codes: rate_limited.","content":{"application/problem+json":{"schema":{"$ref":"#/components/schemas/Problem"}}}},"500":{"description":"Something went wrong. Codes: internal_error.","content":{"application/problem+json":{"schema":{"$ref":"#/components/schemas/Problem"}}}}}}},"/v1/hq/players/{playerId}/ban":{"post":{"operationId":"banPlayerOnPlatform","tags":["hq"],"summary":"Ban a person from the Platform","description":"They cannot sign in anywhere — web, apps or kiosk — and every session they hold is revoked at once; their open realtime sockets are told `access.revoked`. Sent to someone already banned, it changes the reason and expiry. Platform scope: requires `hq:players` and a second factor. One platform-level audit row.","x-clavian-permission":"hq:players","parameters":[{"schema":{"$ref":"#/components/schemas/PlayerId"},"required":true,"description":"A TypeID: \"ply_\" followed by the base32 form of a UUIDv7.","name":"playerId","in":"path"}],"requestBody":{"required":true,"content":{"application/json":{"schema":{"$ref":"#/components/schemas/BanPlayer"}}}},"responses":{"200":{"description":"The platform ban as it now stands.","content":{"application/json":{"schema":{"$ref":"#/components/schemas/PlatformBan"}}}},"400":{"description":"The request could not be understood. Codes: validation_failed.","content":{"application/problem+json":{"schema":{"$ref":"#/components/schemas/Problem"}}}},"401":{"description":"Not signed in. Codes: unauthenticated.","content":{"application/problem+json":{"schema":{"$ref":"#/components/schemas/Problem"}}}},"403":{"description":"Not HQ, no `hq:players`, no second factor on the session, or banning yourself. Codes: forbidden, second_factor_required.","content":{"application/problem+json":{"schema":{"$ref":"#/components/schemas/Problem"}}}},"404":{"description":"No such player. Codes: not_found.","content":{"application/problem+json":{"schema":{"$ref":"#/components/schemas/Problem"}}}},"429":{"description":"Too many requests. Codes: rate_limited.","content":{"application/problem+json":{"schema":{"$ref":"#/components/schemas/Problem"}}}},"500":{"description":"Something went wrong. Codes: internal_error.","content":{"application/problem+json":{"schema":{"$ref":"#/components/schemas/Problem"}}}}}}},"/v1/hq/players/{playerId}/unban":{"post":{"operationId":"unbanPlayerOnPlatform","tags":["hq"],"summary":"Lift a person’s platform ban","description":"They can sign in again; sessions revoked by the ban stay revoked. Platform scope: requires `hq:players` and a second factor. One platform-level audit row.","x-clavian-permission":"hq:players","parameters":[{"schema":{"$ref":"#/components/schemas/PlayerId"},"required":true,"description":"A TypeID: \"ply_\" followed by the base32 form of a UUIDv7.","name":"playerId","in":"path"}],"requestBody":{"required":true,"content":{"application/json":{"schema":{"$ref":"#/components/schemas/LiftBan"}}}},"responses":{"200":{"description":"The platform ban as it now stands: none.","content":{"application/json":{"schema":{"$ref":"#/components/schemas/PlatformBan"}}}},"400":{"description":"The request could not be understood. Codes: validation_failed.","content":{"application/problem+json":{"schema":{"$ref":"#/components/schemas/Problem"}}}},"401":{"description":"Not signed in. Codes: unauthenticated.","content":{"application/problem+json":{"schema":{"$ref":"#/components/schemas/Problem"}}}},"403":{"description":"Not HQ, no `hq:players`, no second factor on the session, or banning yourself. Codes: forbidden, second_factor_required.","content":{"application/problem+json":{"schema":{"$ref":"#/components/schemas/Problem"}}}},"404":{"description":"No such player, or no platform ban on record for them. Codes: not_found.","content":{"application/problem+json":{"schema":{"$ref":"#/components/schemas/Problem"}}}},"429":{"description":"Too many requests. Codes: rate_limited.","content":{"application/problem+json":{"schema":{"$ref":"#/components/schemas/Problem"}}}},"500":{"description":"Something went wrong. Codes: internal_error.","content":{"application/problem+json":{"schema":{"$ref":"#/components/schemas/Problem"}}}}}}},"/v1/hq/players":{"get":{"operationId":"listHqPlayers","tags":["hq"],"summary":"Search every player on the Platform","description":"By phone number, email address or player id (each matched whole), or by part of a name or gamer tag; oldest account first. Platform-level data only, contact details in full. The search is audited: one platform-level row with what was searched for. Platform scope: requires `hq:players_read` and a second factor.","x-clavian-permission":"hq:players_read","parameters":[{"schema":{"type":"string","minLength":1,"description":"The `page.cursor` from the previous response."},"required":false,"description":"The `page.cursor` from the previous response.","name":"cursor","in":"query"},{"schema":{"type":"integer","minimum":1,"maximum":100,"default":25,"description":"Items per page, 1–100."},"required":false,"description":"Items per page, 1–100.","name":"limit","in":"query"},{"schema":{"type":"string","minLength":2,"maxLength":100,"description":"A phone number in E.164 (`+919876543210`), an email address or a player id (`ply_…`), matched exactly; anything else is matched against the name, display name and gamer tag."},"required":true,"description":"A phone number in E.164 (`+919876543210`), an email address or a player id (`ply_…`), matched exactly; anything else is matched against the name, display name and gamer tag.","name":"q","in":"query"}],"responses":{"200":{"description":"A page of players.","content":{"application/json":{"schema":{"$ref":"#/components/schemas/HqPlayerList"}}}},"400":{"description":"The request could not be understood. Codes: validation_failed.","content":{"application/problem+json":{"schema":{"$ref":"#/components/schemas/Problem"}}}},"401":{"description":"Not signed in. Codes: unauthenticated.","content":{"application/problem+json":{"schema":{"$ref":"#/components/schemas/Problem"}}}},"403":{"description":"Not HQ, no `hq:players_read`, or no second factor on the session. Codes: forbidden, second_factor_required.","content":{"application/problem+json":{"schema":{"$ref":"#/components/schemas/Problem"}}}},"429":{"description":"Too many requests. Codes: rate_limited.","content":{"application/problem+json":{"schema":{"$ref":"#/components/schemas/Problem"}}}},"500":{"description":"Something went wrong. Codes: internal_error.","content":{"application/problem+json":{"schema":{"$ref":"#/components/schemas/Problem"}}}}}}},"/v1/hq/players/{playerId}":{"get":{"operationId":"getHqPlayer","tags":["hq"],"summary":"One player’s platform record","description":"The profile, how they sign in, the devices they have signed in on and the platform ban, its reason included; contact details in full. Ban and unban with `POST …/ban` and `…/unban`, which need `hq:players` (ask `GET /v1/me/access` whether you hold it). The read is audited: one platform-level row naming the person. Platform scope: requires `hq:players_read` and a second factor.","x-clavian-permission":"hq:players_read","parameters":[{"schema":{"$ref":"#/components/schemas/PlayerId"},"required":true,"description":"A TypeID: \"ply_\" followed by the base32 form of a UUIDv7.","name":"playerId","in":"path"}],"responses":{"200":{"description":"The player.","content":{"application/json":{"schema":{"$ref":"#/components/schemas/HqPlayer"}}}},"400":{"description":"The request could not be understood. Codes: validation_failed.","content":{"application/problem+json":{"schema":{"$ref":"#/components/schemas/Problem"}}}},"401":{"description":"Not signed in. Codes: unauthenticated.","content":{"application/problem+json":{"schema":{"$ref":"#/components/schemas/Problem"}}}},"403":{"description":"Not HQ, no `hq:players_read`, or no second factor on the session. Codes: forbidden, second_factor_required.","content":{"application/problem+json":{"schema":{"$ref":"#/components/schemas/Problem"}}}},"404":{"description":"No such player. Codes: not_found.","content":{"application/problem+json":{"schema":{"$ref":"#/components/schemas/Problem"}}}},"429":{"description":"Too many requests. Codes: rate_limited.","content":{"application/problem+json":{"schema":{"$ref":"#/components/schemas/Problem"}}}},"500":{"description":"Something went wrong. Codes: internal_error.","content":{"application/problem+json":{"schema":{"$ref":"#/components/schemas/Problem"}}}}}}},"/v1/organizations/{organizationId}/members/{memberId}/status":{"put":{"operationId":"changeMemberStatus","tags":["members"],"summary":"Suspend a member, or reinstate one","description":"The role must be below your own — its permissions a strict subset of yours — and only an owner may grant, change or remove the owner role (spec/07 §6.3). Suspending ends their console access on their next request, as removing them would, and keeps their role and venues; reinstating gives back exactly those. Their account, and signing in as a player, are untouched. Nobody changes their own status, and the last active owner cannot be suspended.","x-clavian-permission":"members:remove","parameters":[{"schema":{"$ref":"#/components/schemas/OrganizationId"},"required":true,"description":"A TypeID: \"org_\" followed by the base32 form of a UUIDv7.","name":"organizationId","in":"path"},{"schema":{"$ref":"#/components/schemas/MemberId"},"required":true,"description":"A TypeID: \"mem_\" followed by the base32 form of a UUIDv7.","name":"memberId","in":"path"}],"requestBody":{"required":true,"content":{"application/json":{"schema":{"$ref":"#/components/schemas/ChangeMemberStatus"}}}},"responses":{"200":{"description":"The member, with the new status.","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Member"}}}},"400":{"description":"The request could not be understood. Codes: validation_failed.","content":{"application/problem+json":{"schema":{"$ref":"#/components/schemas/Problem"}}}},"401":{"description":"Not signed in. Codes: unauthenticated.","content":{"application/problem+json":{"schema":{"$ref":"#/components/schemas/Problem"}}}},"403":{"description":"Not allowed. Codes: forbidden, second_factor_required.","content":{"application/problem+json":{"schema":{"$ref":"#/components/schemas/Problem"}}}},"404":{"description":"No such organisation or member, or not one you can see. Codes: not_found.","content":{"application/problem+json":{"schema":{"$ref":"#/components/schemas/Problem"}}}},"409":{"description":"That would leave the organisation without an active owner. Codes: last_owner.","content":{"application/problem+json":{"schema":{"$ref":"#/components/schemas/Problem"}}}},"429":{"description":"Too many requests. Codes: rate_limited.","content":{"application/problem+json":{"schema":{"$ref":"#/components/schemas/Problem"}}}},"500":{"description":"Something went wrong. Codes: internal_error.","content":{"application/problem+json":{"schema":{"$ref":"#/components/schemas/Problem"}}}}}}},"/v1/organizations/{organizationId}/roles/{role}":{"get":{"operationId":"getRole","tags":["members"],"summary":"One role: its permissions and its limits","description":"A built-in role of spec/07 §6.2 or a custom role of the organisation.","x-clavian-permission":"members:read","parameters":[{"schema":{"$ref":"#/components/schemas/OrganizationId"},"required":true,"description":"A TypeID: \"org_\" followed by the base32 form of a UUIDv7.","name":"organizationId","in":"path"},{"schema":{"$ref":"#/components/schemas/RoleName"},"required":true,"description":"`owner`, `manager`, `cashier`, … or a custom role of the organisation.","name":"role","in":"path"}],"responses":{"200":{"description":"The role.","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Role"}}}},"400":{"description":"The request could not be understood. Codes: validation_failed.","content":{"application/problem+json":{"schema":{"$ref":"#/components/schemas/Problem"}}}},"401":{"description":"Not signed in. Codes: unauthenticated.","content":{"application/problem+json":{"schema":{"$ref":"#/components/schemas/Problem"}}}},"403":{"description":"Not allowed. Codes: forbidden, second_factor_required.","content":{"application/problem+json":{"schema":{"$ref":"#/components/schemas/Problem"}}}},"404":{"description":"No such role in this organisation. Codes: not_found.","content":{"application/problem+json":{"schema":{"$ref":"#/components/schemas/Problem"}}}},"429":{"description":"Too many requests. Codes: rate_limited.","content":{"application/problem+json":{"schema":{"$ref":"#/components/schemas/Problem"}}}},"500":{"description":"Something went wrong. Codes: internal_error.","content":{"application/problem+json":{"schema":{"$ref":"#/components/schemas/Problem"}}}}}},"put":{"operationId":"updateRole","tags":["members"],"summary":"Change a custom role’s permissions","description":"Only permissions you hold yourself, and the role must stay below your own — an owner is not bound by that. A custom role reaches its holders at every venue, so only someone who works at every venue makes, changes or deletes one (spec/07 §6.2–§6.3). Both the role as it is and as it will be must be below your own. Its holders’ consoles are told at once (`access.changed`), and their next request is answered with the new set. A built-in role is defined in code and cannot be changed.","x-clavian-permission":"members:roles","parameters":[{"schema":{"$ref":"#/components/schemas/OrganizationId"},"required":true,"description":"A TypeID: \"org_\" followed by the base32 form of a UUIDv7.","name":"organizationId","in":"path"},{"schema":{"$ref":"#/components/schemas/RoleName"},"required":true,"description":"`owner`, `manager`, `cashier`, … or a custom role of the organisation.","name":"role","in":"path"}],"requestBody":{"required":true,"content":{"application/json":{"schema":{"$ref":"#/components/schemas/UpdateRole"}}}},"responses":{"200":{"description":"The role, with its new permissions.","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Role"}}}},"400":{"description":"The request could not be understood. Codes: validation_failed.","content":{"application/problem+json":{"schema":{"$ref":"#/components/schemas/Problem"}}}},"401":{"description":"Not signed in. Codes: unauthenticated.","content":{"application/problem+json":{"schema":{"$ref":"#/components/schemas/Problem"}}}},"403":{"description":"Not allowed. Codes: forbidden, second_factor_required.","content":{"application/problem+json":{"schema":{"$ref":"#/components/schemas/Problem"}}}},"404":{"description":"No such role in this organisation. Codes: not_found.","content":{"application/problem+json":{"schema":{"$ref":"#/components/schemas/Problem"}}}},"429":{"description":"Too many requests. Codes: rate_limited.","content":{"application/problem+json":{"schema":{"$ref":"#/components/schemas/Problem"}}}},"500":{"description":"Something went wrong. Codes: internal_error.","content":{"application/problem+json":{"schema":{"$ref":"#/components/schemas/Problem"}}}}}},"delete":{"operationId":"deleteRole","tags":["members"],"summary":"Delete a custom role","description":"Only permissions you hold yourself, and the role must stay below your own — an owner is not bound by that. A custom role reaches its holders at every venue, so only someone who works at every venue makes, changes or deletes one (spec/07 §6.2–§6.3). Refused while any member holds it — a suspended one included — or a pending invitation names it: give them another role first. Its limits go with it.","x-clavian-permission":"members:roles","parameters":[{"schema":{"$ref":"#/components/schemas/OrganizationId"},"required":true,"description":"A TypeID: \"org_\" followed by the base32 form of a UUIDv7.","name":"organizationId","in":"path"},{"schema":{"$ref":"#/components/schemas/RoleName"},"required":true,"description":"`owner`, `manager`, `cashier`, … or a custom role of the organisation.","name":"role","in":"path"}],"responses":{"204":{"description":"Deleted."},"400":{"description":"The request could not be understood. Codes: validation_failed.","content":{"application/problem+json":{"schema":{"$ref":"#/components/schemas/Problem"}}}},"401":{"description":"Not signed in. Codes: unauthenticated.","content":{"application/problem+json":{"schema":{"$ref":"#/components/schemas/Problem"}}}},"403":{"description":"Not allowed. Codes: forbidden, second_factor_required.","content":{"application/problem+json":{"schema":{"$ref":"#/components/schemas/Problem"}}}},"404":{"description":"No such role in this organisation. Codes: not_found.","content":{"application/problem+json":{"schema":{"$ref":"#/components/schemas/Problem"}}}},"409":{"description":"A member holds the role, or a pending invitation names it. Codes: role_in_use.","content":{"application/problem+json":{"schema":{"$ref":"#/components/schemas/Problem"}}}},"429":{"description":"Too many requests. Codes: rate_limited.","content":{"application/problem+json":{"schema":{"$ref":"#/components/schemas/Problem"}}}},"500":{"description":"Something went wrong. Codes: internal_error.","content":{"application/problem+json":{"schema":{"$ref":"#/components/schemas/Problem"}}}}}}},"/v1/organizations/{organizationId}/roles/{role}/limits":{"put":{"operationId":"changeRoleLimits","tags":["members"],"summary":"Set the limits a role carries","description":"The role must be below your own — its permissions a strict subset of yours — and only an owner may grant, change or remove the owner role (spec/07 §6.3). Every limit is given, `null` for none of the role’s own. Nobody gives a limit higher than their own role’s, and because a role’s limits reach its holders at every venue, only someone who works at every venue sets them. The owner role carries none. Amounts are in the organisation’s currency.","x-clavian-permission":"members:roles","parameters":[{"schema":{"$ref":"#/components/schemas/OrganizationId"},"required":true,"description":"A TypeID: \"org_\" followed by the base32 form of a UUIDv7.","name":"organizationId","in":"path"},{"schema":{"$ref":"#/components/schemas/RoleName"},"required":true,"description":"`owner`, `manager`, `cashier`, … or a custom role of the organisation.","name":"role","in":"path"}],"requestBody":{"required":true,"content":{"application/json":{"schema":{"$ref":"#/components/schemas/RoleLimits"}}}},"responses":{"200":{"description":"The role, with its new limits.","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Role"}}}},"400":{"description":"The request could not be understood. Codes: validation_failed.","content":{"application/problem+json":{"schema":{"$ref":"#/components/schemas/Problem"}}}},"401":{"description":"Not signed in. Codes: unauthenticated.","content":{"application/problem+json":{"schema":{"$ref":"#/components/schemas/Problem"}}}},"403":{"description":"Not allowed. Codes: forbidden, second_factor_required.","content":{"application/problem+json":{"schema":{"$ref":"#/components/schemas/Problem"}}}},"404":{"description":"No such role in this organisation. Codes: not_found.","content":{"application/problem+json":{"schema":{"$ref":"#/components/schemas/Problem"}}}},"429":{"description":"Too many requests. Codes: rate_limited.","content":{"application/problem+json":{"schema":{"$ref":"#/components/schemas/Problem"}}}},"500":{"description":"Something went wrong. Codes: internal_error.","content":{"application/problem+json":{"schema":{"$ref":"#/components/schemas/Problem"}}}}}}},"/v1/organizations/{organizationId}/players/{playerId}/id-checks":{"post":{"operationId":"recordIdCheck","tags":["players"],"summary":"Record a staff document ID check","description":"The member looked at a physical ID: records its type, the last four characters of its number and, optionally, a photo of it, which is encrypted into the private bucket and deleted after `compliance.idPhotoRetentionDays`. The check counts for this organisation only. Needs `players:id_check` and an `Idempotency-Key`: a retry with the same key and body answers the check already made (`201`, `Idempotent-Replayed: true`) and records nothing twice. One audit row.","x-clavian-permission":"players:id_check","parameters":[{"schema":{"$ref":"#/components/schemas/OrganizationId"},"required":true,"description":"A TypeID: \"org_\" followed by the base32 form of a UUIDv7.","name":"organizationId","in":"path"},{"schema":{"$ref":"#/components/schemas/PlayerId"},"required":true,"description":"A TypeID: \"ply_\" followed by the base32 form of a UUIDv7.","name":"playerId","in":"path"},{"schema":{"$ref":"#/components/schemas/IdempotencyKey"},"required":true,"description":"Client-generated UUID, unique per logical create.","name":"idempotency-key","in":"header"}],"requestBody":{"required":true,"content":{"application/json":{"schema":{"$ref":"#/components/schemas/RecordIdCheck"}}}},"responses":{"201":{"description":"The check — new, or the one this key already made.","content":{"application/json":{"schema":{"$ref":"#/components/schemas/IdCheck"}}}},"400":{"description":"The request could not be understood. Codes: validation_failed.","content":{"application/problem+json":{"schema":{"$ref":"#/components/schemas/Problem"}}}},"401":{"description":"Not signed in. Codes: unauthenticated.","content":{"application/problem+json":{"schema":{"$ref":"#/components/schemas/Problem"}}}},"403":{"description":"Your role does not allow this: it needs `players:id_check`. Codes: forbidden, second_factor_required.","content":{"application/problem+json":{"schema":{"$ref":"#/components/schemas/Problem"}}}},"404":{"description":"No such organisation or player, or not one you can see. Codes: not_found.","content":{"application/problem+json":{"schema":{"$ref":"#/components/schemas/Problem"}}}},"409":{"description":"The same key is being used by another request at this moment; send it again. Codes: conflict.","content":{"application/problem+json":{"schema":{"$ref":"#/components/schemas/Problem"}}}},"422":{"description":"The key was already used for a different check, or the photo is not the image it says it is. Codes: idempotency_key_reused, unprocessable.","content":{"application/problem+json":{"schema":{"$ref":"#/components/schemas/Problem"}}}},"429":{"description":"Too many requests. Codes: rate_limited.","content":{"application/problem+json":{"schema":{"$ref":"#/components/schemas/Problem"}}}},"500":{"description":"Something went wrong. Codes: internal_error.","content":{"application/problem+json":{"schema":{"$ref":"#/components/schemas/Problem"}}}}}},"get":{"operationId":"listIdChecks","tags":["players"],"summary":"A player’s ID checks at this organisation","description":"Newest first: the staff document checks this organisation made, with who checked and whether a photo is kept. Never another organisation’s. The ID checks tab of the player profile (spec/13 §8). Needs `players:id_check`.","x-clavian-permission":"players:id_check","parameters":[{"schema":{"$ref":"#/components/schemas/OrganizationId"},"required":true,"description":"A TypeID: \"org_\" followed by the base32 form of a UUIDv7.","name":"organizationId","in":"path"},{"schema":{"$ref":"#/components/schemas/PlayerId"},"required":true,"description":"A TypeID: \"ply_\" followed by the base32 form of a UUIDv7.","name":"playerId","in":"path"},{"schema":{"type":"string","minLength":1,"description":"The `page.cursor` from the previous response."},"required":false,"description":"The `page.cursor` from the previous response.","name":"cursor","in":"query"},{"schema":{"type":"integer","minimum":1,"maximum":100,"default":25,"description":"Items per page, 1–100."},"required":false,"description":"Items per page, 1–100.","name":"limit","in":"query"}],"responses":{"200":{"description":"A page of checks.","content":{"application/json":{"schema":{"$ref":"#/components/schemas/IdCheckList"}}}},"400":{"description":"The request could not be understood. Codes: validation_failed.","content":{"application/problem+json":{"schema":{"$ref":"#/components/schemas/Problem"}}}},"401":{"description":"Not signed in. Codes: unauthenticated.","content":{"application/problem+json":{"schema":{"$ref":"#/components/schemas/Problem"}}}},"403":{"description":"Your role does not allow this: it needs `players:id_check`. Codes: forbidden, second_factor_required.","content":{"application/problem+json":{"schema":{"$ref":"#/components/schemas/Problem"}}}},"404":{"description":"No such organisation or player, or not one you can see. Codes: not_found.","content":{"application/problem+json":{"schema":{"$ref":"#/components/schemas/Problem"}}}},"429":{"description":"Too many requests. Codes: rate_limited.","content":{"application/problem+json":{"schema":{"$ref":"#/components/schemas/Problem"}}}},"500":{"description":"Something went wrong. Codes: internal_error.","content":{"application/problem+json":{"schema":{"$ref":"#/components/schemas/Problem"}}}}}}},"/v1/organizations/{organizationId}/players/{playerId}/id-checks/{idCheckId}/photo":{"get":{"operationId":"getIdCheckPhoto","tags":["players"],"summary":"View an ID check’s photo","description":"A signed URL to the photo, valid for 60 seconds. Every call writes one audit row, so ask only when the photo is about to be shown. Needs `players:id_check`. A photo the retention job has deleted, or a check made without one, is `404`.","x-clavian-permission":"players:id_check","parameters":[{"schema":{"$ref":"#/components/schemas/OrganizationId"},"required":true,"description":"A TypeID: \"org_\" followed by the base32 form of a UUIDv7.","name":"organizationId","in":"path"},{"schema":{"$ref":"#/components/schemas/PlayerId"},"required":true,"description":"A TypeID: \"ply_\" followed by the base32 form of a UUIDv7.","name":"playerId","in":"path"},{"schema":{"$ref":"#/components/schemas/IdCheckId"},"required":true,"description":"A TypeID: \"idc_\" followed by the base32 form of a UUIDv7.","name":"idCheckId","in":"path"}],"responses":{"200":{"description":"Where to fetch the photo, for the next minute.","content":{"application/json":{"schema":{"$ref":"#/components/schemas/IdCheckPhotoLink"}}}},"400":{"description":"The request could not be understood. Codes: validation_failed.","content":{"application/problem+json":{"schema":{"$ref":"#/components/schemas/Problem"}}}},"401":{"description":"Not signed in. Codes: unauthenticated.","content":{"application/problem+json":{"schema":{"$ref":"#/components/schemas/Problem"}}}},"403":{"description":"Your role does not allow this: it needs `players:id_check`. Codes: forbidden, second_factor_required.","content":{"application/problem+json":{"schema":{"$ref":"#/components/schemas/Problem"}}}},"404":{"description":"No such check, or no photo kept with it. Codes: not_found.","content":{"application/problem+json":{"schema":{"$ref":"#/components/schemas/Problem"}}}},"429":{"description":"Too many requests. Codes: rate_limited.","content":{"application/problem+json":{"schema":{"$ref":"#/components/schemas/Problem"}}}},"500":{"description":"Something went wrong. Codes: internal_error.","content":{"application/problem+json":{"schema":{"$ref":"#/components/schemas/Problem"}}}}}}},"/v1/files/{fileId}":{"get":{"operationId":"readSignedFile","tags":["files"],"summary":"Fetch a private file through its signed URL","description":"The URL `getIdCheckPhoto` answers, used as it is — an `<img src>`, say. No session: the signature, over the file, its organisation and the expiry, is the authority, and it lapses 60 seconds after it was issued. Answers the decrypted image with `Cache-Control: no-store`. Not a call a client makes by itself.","parameters":[{"schema":{"$ref":"#/components/schemas/FileId"},"required":true,"description":"A TypeID: \"fil_\" followed by the base32 form of a UUIDv7.","name":"fileId","in":"path"},{"schema":{"$ref":"#/components/schemas/OrganizationId"},"required":true,"description":"A TypeID: \"org_\" followed by the base32 form of a UUIDv7.","name":"organizationId","in":"query"},{"schema":{"type":"integer","exclusiveMinimum":0},"required":true,"name":"expires","in":"query"},{"schema":{"type":"string","minLength":1,"maxLength":128},"required":true,"name":"signature","in":"query"}],"responses":{"200":{"description":"The image.","content":{"image/jpeg":{"schema":{"type":"string","format":"binary"}},"image/png":{"schema":{"type":"string","format":"binary"}},"image/webp":{"schema":{"type":"string","format":"binary"}}}},"400":{"description":"The request could not be understood. Codes: validation_failed.","content":{"application/problem+json":{"schema":{"$ref":"#/components/schemas/Problem"}}}},"403":{"description":"The signature is wrong or has expired. Codes: forbidden.","content":{"application/problem+json":{"schema":{"$ref":"#/components/schemas/Problem"}}}},"404":{"description":"No such file, or it has been deleted. Codes: not_found.","content":{"application/problem+json":{"schema":{"$ref":"#/components/schemas/Problem"}}}},"429":{"description":"Too many requests. Codes: rate_limited.","content":{"application/problem+json":{"schema":{"$ref":"#/components/schemas/Problem"}}}},"500":{"description":"Something went wrong. Codes: internal_error.","content":{"application/problem+json":{"schema":{"$ref":"#/components/schemas/Problem"}}}}}}},"/v1/organizations/{organizationId}/roles":{"get":{"operationId":"listRoles","tags":["members"],"summary":"Every role: the built-in ones and the custom ones","description":"Each with its permissions and limits — what the Roles screen renders. The built-in roles’ grants are the table in `@clavian/contracts`, so the screen and the server read one list.","x-clavian-permission":"members:read","parameters":[{"schema":{"$ref":"#/components/schemas/OrganizationId"},"required":true,"description":"A TypeID: \"org_\" followed by the base32 form of a UUIDv7.","name":"organizationId","in":"path"}],"responses":{"200":{"description":"The roles.","content":{"application/json":{"schema":{"$ref":"#/components/schemas/RoleList"}}}},"400":{"description":"The request could not be understood. Codes: validation_failed.","content":{"application/problem+json":{"schema":{"$ref":"#/components/schemas/Problem"}}}},"401":{"description":"Not signed in. Codes: unauthenticated.","content":{"application/problem+json":{"schema":{"$ref":"#/components/schemas/Problem"}}}},"403":{"description":"Not allowed. Codes: forbidden, second_factor_required.","content":{"application/problem+json":{"schema":{"$ref":"#/components/schemas/Problem"}}}},"404":{"description":"No such organisation or member, or not one you can see. Codes: not_found.","content":{"application/problem+json":{"schema":{"$ref":"#/components/schemas/Problem"}}}},"429":{"description":"Too many requests. Codes: rate_limited.","content":{"application/problem+json":{"schema":{"$ref":"#/components/schemas/Problem"}}}},"500":{"description":"Something went wrong. Codes: internal_error.","content":{"application/problem+json":{"schema":{"$ref":"#/components/schemas/Problem"}}}}}},"post":{"operationId":"createRole","tags":["members"],"summary":"Make a custom role","description":"Only permissions you hold yourself, and the role must stay below your own — an owner is not bound by that. A custom role reaches its holders at every venue, so only someone who works at every venue makes, changes or deletes one (spec/07 §6.2–§6.3). At most 20 custom roles per organisation. It starts with no limits of its own.","x-clavian-permission":"members:roles","parameters":[{"schema":{"$ref":"#/components/schemas/OrganizationId"},"required":true,"description":"A TypeID: \"org_\" followed by the base32 form of a UUIDv7.","name":"organizationId","in":"path"}],"requestBody":{"required":true,"content":{"application/json":{"schema":{"$ref":"#/components/schemas/CreateRole"}}}},"responses":{"201":{"description":"The new role.","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Role"}}}},"400":{"description":"The request could not be understood. Codes: validation_failed.","content":{"application/problem+json":{"schema":{"$ref":"#/components/schemas/Problem"}}}},"401":{"description":"Not signed in. Codes: unauthenticated.","content":{"application/problem+json":{"schema":{"$ref":"#/components/schemas/Problem"}}}},"403":{"description":"Not allowed. Codes: forbidden, second_factor_required.","content":{"application/problem+json":{"schema":{"$ref":"#/components/schemas/Problem"}}}},"404":{"description":"No such organisation or member, or not one you can see. Codes: not_found.","content":{"application/problem+json":{"schema":{"$ref":"#/components/schemas/Problem"}}}},"409":{"description":"A role by that name exists, or the organisation already has as many custom roles as it may. Codes: role_name_taken, custom_role_limit_reached.","content":{"application/problem+json":{"schema":{"$ref":"#/components/schemas/Problem"}}}},"429":{"description":"Too many requests. Codes: rate_limited.","content":{"application/problem+json":{"schema":{"$ref":"#/components/schemas/Problem"}}}},"500":{"description":"Something went wrong. Codes: internal_error.","content":{"application/problem+json":{"schema":{"$ref":"#/components/schemas/Problem"}}}}}}},"/v1/me/access":{"get":{"operationId":"getMyAccess","tags":["me"],"summary":"What you may do: in an organisation, at a venue, or on the platform","description":"Needs a signed-in user; read from the live session, never the cookie cache. With `organizationId` or `venueId`, the member’s role, its effective permissions and its limits there; with neither, the platform role’s. A venue you do not work at, or an organisation you are not a member of, is `404`. On a shared counter device it is the PIN holder’s, and `device-locked` while nobody holds the PIN.","parameters":[{"schema":{"$ref":"#/components/schemas/OrganizationId"},"required":false,"description":"A TypeID: \"org_\" followed by the base32 form of a UUIDv7.","name":"organizationId","in":"query"},{"schema":{"$ref":"#/components/schemas/VenueId"},"required":false,"description":"A TypeID: \"ven_\" followed by the base32 form of a UUIDv7.","name":"venueId","in":"query"}],"responses":{"200":{"description":"The effective access.","content":{"application/json":{"schema":{"$ref":"#/components/schemas/MyAccess"}}}},"400":{"description":"The request could not be understood. Codes: validation_failed.","content":{"application/problem+json":{"schema":{"$ref":"#/components/schemas/Problem"}}}},"401":{"description":"Not signed in. Codes: unauthenticated.","content":{"application/problem+json":{"schema":{"$ref":"#/components/schemas/Problem"}}}},"403":{"description":"A shared counter device nobody has unlocked. Codes: device_locked.","content":{"application/problem+json":{"schema":{"$ref":"#/components/schemas/Problem"}}}},"404":{"description":"No such organisation or venue, or not one you work at. Codes: not_found.","content":{"application/problem+json":{"schema":{"$ref":"#/components/schemas/Problem"}}}},"429":{"description":"Too many requests. Codes: rate_limited.","content":{"application/problem+json":{"schema":{"$ref":"#/components/schemas/Problem"}}}},"500":{"description":"Something went wrong. Codes: internal_error.","content":{"application/problem+json":{"schema":{"$ref":"#/components/schemas/Problem"}}}}}}},"/v1/me/organizations":{"get":{"operationId":"listMyOrganizations","tags":["me"],"summary":"The organisations you are on the staff of","description":"Needs a signed-in user. Active memberships only: a suspended or removed member sees nothing of that organisation. With each, the role held there, whether you work at every venue, and whether its console needs a second factor. On a shared counter device, the PIN holder’s membership of the device’s organisation.","responses":{"200":{"description":"Your organisations.","content":{"application/json":{"schema":{"$ref":"#/components/schemas/MyOrganizationList"}}}},"401":{"description":"Not signed in. Codes: unauthenticated.","content":{"application/problem+json":{"schema":{"$ref":"#/components/schemas/Problem"}}}},"403":{"description":"A shared counter device nobody has unlocked. Codes: device_locked.","content":{"application/problem+json":{"schema":{"$ref":"#/components/schemas/Problem"}}}},"429":{"description":"Too many requests. Codes: rate_limited.","content":{"application/problem+json":{"schema":{"$ref":"#/components/schemas/Problem"}}}},"500":{"description":"Something went wrong. Codes: internal_error.","content":{"application/problem+json":{"schema":{"$ref":"#/components/schemas/Problem"}}}}}}},"/v1/me/venues":{"get":{"operationId":"listMyVenues","tags":["me"],"summary":"The venues you work at","description":"Needs a signed-in user. The live venues of your organisations that your venue scope covers — every venue for a member with no scope, only those venues for one with a scope — and never an archived one. `organizationId` narrows it to one organisation. On a shared counter device, the device’s venue.","parameters":[{"schema":{"$ref":"#/components/schemas/OrganizationId"},"required":false,"description":"A TypeID: \"org_\" followed by the base32 form of a UUIDv7.","name":"organizationId","in":"query"}],"responses":{"200":{"description":"Your venues.","content":{"application/json":{"schema":{"$ref":"#/components/schemas/MyVenueList"}}}},"400":{"description":"The request could not be understood. Codes: validation_failed.","content":{"application/problem+json":{"schema":{"$ref":"#/components/schemas/Problem"}}}},"401":{"description":"Not signed in. Codes: unauthenticated.","content":{"application/problem+json":{"schema":{"$ref":"#/components/schemas/Problem"}}}},"403":{"description":"A shared counter device nobody has unlocked. Codes: device_locked.","content":{"application/problem+json":{"schema":{"$ref":"#/components/schemas/Problem"}}}},"404":{"description":"No such organisation, or not one you are on the staff of. Codes: not_found.","content":{"application/problem+json":{"schema":{"$ref":"#/components/schemas/Problem"}}}},"429":{"description":"Too many requests. Codes: rate_limited.","content":{"application/problem+json":{"schema":{"$ref":"#/components/schemas/Problem"}}}},"500":{"description":"Something went wrong. Codes: internal_error.","content":{"application/problem+json":{"schema":{"$ref":"#/components/schemas/Problem"}}}}}}},"/v1/organizations/{organizationId}/members/me/pin":{"put":{"operationId":"setMyStaffPin","tags":["members"],"summary":"Set or change your own staff PIN at a venue","description":"From your own signed-in session — never from a counter device — for a venue you work at. Setting a new PIN replaces the old one. A PIN locked after five wrong tries stays locked until a manager resets it.","parameters":[{"schema":{"$ref":"#/components/schemas/OrganizationId"},"required":true,"description":"A TypeID: \"org_\" followed by the base32 form of a UUIDv7.","name":"organizationId","in":"path"},{"schema":{"allOf":[{"$ref":"#/components/schemas/VenueId"},{"description":"The venue the PIN is for, or the counter device stands at."}]},"required":true,"description":"The venue the PIN is for, or the counter device stands at.","name":"x-clavian-venue","in":"header"}],"requestBody":{"required":true,"content":{"application/json":{"schema":{"$ref":"#/components/schemas/SetStaffPin"}}}},"responses":{"204":{"description":"Set."},"400":{"description":"The request could not be understood. Codes: validation_failed.","content":{"application/problem+json":{"schema":{"$ref":"#/components/schemas/Problem"}}}},"401":{"description":"Not signed in. Codes: unauthenticated.","content":{"application/problem+json":{"schema":{"$ref":"#/components/schemas/Problem"}}}},"403":{"description":"Your PIN here is locked, or this is a counter device rather than your own session. Codes: staff_pin_locked, shared_device.","content":{"application/problem+json":{"schema":{"$ref":"#/components/schemas/Problem"}}}},"404":{"description":"No such organisation, venue or member, or not one you can see. Codes: not_found.","content":{"application/problem+json":{"schema":{"$ref":"#/components/schemas/Problem"}}}},"429":{"description":"Too many requests. Codes: rate_limited.","content":{"application/problem+json":{"schema":{"$ref":"#/components/schemas/Problem"}}}},"500":{"description":"Something went wrong. Codes: internal_error.","content":{"application/problem+json":{"schema":{"$ref":"#/components/schemas/Problem"}}}}}}},"/v1/organizations/{organizationId}/members/{memberId}/pin":{"delete":{"operationId":"resetStaffPin","tags":["members"],"summary":"Reset a member’s staff PIN at a venue","description":"Clears their PIN and its lock; they set a new one from their own session. The member’s role must be below your own, and they must work only at venues you do (spec/07 §6.5). Nobody resets their own.","x-clavian-permission":"members:update","parameters":[{"schema":{"$ref":"#/components/schemas/OrganizationId"},"required":true,"description":"A TypeID: \"org_\" followed by the base32 form of a UUIDv7.","name":"organizationId","in":"path"},{"schema":{"$ref":"#/components/schemas/MemberId"},"required":true,"description":"A TypeID: \"mem_\" followed by the base32 form of a UUIDv7.","name":"memberId","in":"path"},{"schema":{"allOf":[{"$ref":"#/components/schemas/VenueId"},{"description":"The venue the PIN is for, or the counter device stands at."}]},"required":true,"description":"The venue the PIN is for, or the counter device stands at.","name":"x-clavian-venue","in":"header"}],"responses":{"204":{"description":"Reset, or there was no PIN to reset."},"400":{"description":"The request could not be understood. Codes: validation_failed.","content":{"application/problem+json":{"schema":{"$ref":"#/components/schemas/Problem"}}}},"401":{"description":"Not signed in. Codes: unauthenticated.","content":{"application/problem+json":{"schema":{"$ref":"#/components/schemas/Problem"}}}},"403":{"description":"Not allowed. Codes: forbidden, second_factor_required.","content":{"application/problem+json":{"schema":{"$ref":"#/components/schemas/Problem"}}}},"404":{"description":"No such organisation, venue or member, or not one you can see. Codes: not_found.","content":{"application/problem+json":{"schema":{"$ref":"#/components/schemas/Problem"}}}},"429":{"description":"Too many requests. Codes: rate_limited.","content":{"application/problem+json":{"schema":{"$ref":"#/components/schemas/Problem"}}}},"500":{"description":"Something went wrong. Codes: internal_error.","content":{"application/problem+json":{"schema":{"$ref":"#/components/schemas/Problem"}}}}}}},"/v1/organizations/{organizationId}/members/shared-device":{"post":{"operationId":"openSharedDevice","tags":["members"],"summary":"Turn this device into a shared counter device","description":"The session making the request stops being yours and becomes the counter device’s, at the venue named: locked until someone unlocks it with their PIN. Your own account can no longer be changed from it; sign it out to end counter mode. The second factor the session was signed in with stays with it, and is what the console’s second-factor rule reads for every PIN holder.","x-clavian-permission":"members:update","parameters":[{"schema":{"$ref":"#/components/schemas/OrganizationId"},"required":true,"description":"A TypeID: \"org_\" followed by the base32 form of a UUIDv7.","name":"organizationId","in":"path"},{"schema":{"allOf":[{"$ref":"#/components/schemas/VenueId"},{"description":"The venue the PIN is for, or the counter device stands at."}]},"required":true,"description":"The venue the PIN is for, or the counter device stands at.","name":"x-clavian-venue","in":"header"}],"responses":{"200":{"description":"The counter device, locked.","content":{"application/json":{"schema":{"$ref":"#/components/schemas/SharedDevice"}}}},"400":{"description":"The request could not be understood. Codes: validation_failed.","content":{"application/problem+json":{"schema":{"$ref":"#/components/schemas/Problem"}}}},"401":{"description":"Not signed in. Codes: unauthenticated.","content":{"application/problem+json":{"schema":{"$ref":"#/components/schemas/Problem"}}}},"403":{"description":"This session is already a counter device, or lacks the permission. Codes: shared_device, forbidden.","content":{"application/problem+json":{"schema":{"$ref":"#/components/schemas/Problem"}}}},"404":{"description":"No such organisation, venue or member, or not one you can see. Codes: not_found.","content":{"application/problem+json":{"schema":{"$ref":"#/components/schemas/Problem"}}}},"429":{"description":"Too many requests. Codes: rate_limited.","content":{"application/problem+json":{"schema":{"$ref":"#/components/schemas/Problem"}}}},"500":{"description":"Something went wrong. Codes: internal_error.","content":{"application/problem+json":{"schema":{"$ref":"#/components/schemas/Problem"}}}}}},"get":{"operationId":"getSharedDevice","tags":["members"],"summary":"This counter device: who holds it, and who can unlock it","description":"Answers a locked counter device too — it is what the PIN pad is drawn from. Not a counter device: `404`.","parameters":[{"schema":{"$ref":"#/components/schemas/OrganizationId"},"required":true,"description":"A TypeID: \"org_\" followed by the base32 form of a UUIDv7.","name":"organizationId","in":"path"},{"schema":{"allOf":[{"$ref":"#/components/schemas/VenueId"},{"description":"The venue the PIN is for, or the counter device stands at."}]},"required":true,"description":"The venue the PIN is for, or the counter device stands at.","name":"x-clavian-venue","in":"header"}],"responses":{"200":{"description":"The counter device.","content":{"application/json":{"schema":{"$ref":"#/components/schemas/SharedDevice"}}}},"400":{"description":"The request could not be understood. Codes: validation_failed.","content":{"application/problem+json":{"schema":{"$ref":"#/components/schemas/Problem"}}}},"401":{"description":"Not signed in. Codes: unauthenticated.","content":{"application/problem+json":{"schema":{"$ref":"#/components/schemas/Problem"}}}},"404":{"description":"No such organisation, venue or member, or not one you can see. Codes: not_found.","content":{"application/problem+json":{"schema":{"$ref":"#/components/schemas/Problem"}}}},"429":{"description":"Too many requests. Codes: rate_limited.","content":{"application/problem+json":{"schema":{"$ref":"#/components/schemas/Problem"}}}},"500":{"description":"Something went wrong. Codes: internal_error.","content":{"application/problem+json":{"schema":{"$ref":"#/components/schemas/Problem"}}}}}}},"/v1/organizations/{organizationId}/members/{memberId}/unlock":{"post":{"operationId":"unlockSharedDevice","tags":["members"],"summary":"Unlock this counter device as a member, with their PIN","description":"Only on a counter device, and only for a member who works at its venue and has a PIN there. From then on every request the device makes is theirs, until it is locked, someone else unlocks it, or it has been idle for `staff.pinSessionMinutes`. 5 wrong PINs in a row lock that member’s PIN until a manager resets it; everyone else can still unlock.","parameters":[{"schema":{"$ref":"#/components/schemas/OrganizationId"},"required":true,"description":"A TypeID: \"org_\" followed by the base32 form of a UUIDv7.","name":"organizationId","in":"path"},{"schema":{"$ref":"#/components/schemas/MemberId"},"required":true,"description":"A TypeID: \"mem_\" followed by the base32 form of a UUIDv7.","name":"memberId","in":"path"},{"schema":{"allOf":[{"$ref":"#/components/schemas/VenueId"},{"description":"The venue the PIN is for, or the counter device stands at."}]},"required":true,"description":"The venue the PIN is for, or the counter device stands at.","name":"x-clavian-venue","in":"header"}],"requestBody":{"required":true,"content":{"application/json":{"schema":{"$ref":"#/components/schemas/UnlockSharedDevice"}}}},"responses":{"200":{"description":"The counter device, unlocked for them.","content":{"application/json":{"schema":{"$ref":"#/components/schemas/SharedDevice"}}}},"400":{"description":"The PIN is wrong, or the member has no PIN at this venue. Codes: staff_pin_incorrect, validation_failed.","content":{"application/problem+json":{"schema":{"$ref":"#/components/schemas/Problem"}}}},"401":{"description":"Not signed in. Codes: unauthenticated.","content":{"application/problem+json":{"schema":{"$ref":"#/components/schemas/Problem"}}}},"403":{"description":"That member’s PIN is locked; a manager must reset it. Codes: staff_pin_locked.","content":{"application/problem+json":{"schema":{"$ref":"#/components/schemas/Problem"}}}},"404":{"description":"No such organisation, venue or member, or not one you can see. Codes: not_found.","content":{"application/problem+json":{"schema":{"$ref":"#/components/schemas/Problem"}}}},"429":{"description":"Too many requests. Codes: rate_limited.","content":{"application/problem+json":{"schema":{"$ref":"#/components/schemas/Problem"}}}},"500":{"description":"Something went wrong. Codes: internal_error.","content":{"application/problem+json":{"schema":{"$ref":"#/components/schemas/Problem"}}}}}}},"/v1/organizations/{organizationId}/members/shared-device/lock":{"post":{"operationId":"lockSharedDevice","tags":["members"],"summary":"Lock this counter device","description":"The PIN holder steps away; the next person unlocks with their own PIN. Locking a locked device changes nothing.","parameters":[{"schema":{"$ref":"#/components/schemas/OrganizationId"},"required":true,"description":"A TypeID: \"org_\" followed by the base32 form of a UUIDv7.","name":"organizationId","in":"path"},{"schema":{"allOf":[{"$ref":"#/components/schemas/VenueId"},{"description":"The venue the PIN is for, or the counter device stands at."}]},"required":true,"description":"The venue the PIN is for, or the counter device stands at.","name":"x-clavian-venue","in":"header"}],"responses":{"200":{"description":"The counter device, locked.","content":{"application/json":{"schema":{"$ref":"#/components/schemas/SharedDevice"}}}},"400":{"description":"The request could not be understood. Codes: validation_failed.","content":{"application/problem+json":{"schema":{"$ref":"#/components/schemas/Problem"}}}},"401":{"description":"Not signed in. Codes: unauthenticated.","content":{"application/problem+json":{"schema":{"$ref":"#/components/schemas/Problem"}}}},"404":{"description":"No such organisation, venue or member, or not one you can see. Codes: not_found.","content":{"application/problem+json":{"schema":{"$ref":"#/components/schemas/Problem"}}}},"429":{"description":"Too many requests. Codes: rate_limited.","content":{"application/problem+json":{"schema":{"$ref":"#/components/schemas/Problem"}}}},"500":{"description":"Something went wrong. Codes: internal_error.","content":{"application/problem+json":{"schema":{"$ref":"#/components/schemas/Problem"}}}}}}},"/v1/me/contact-verification":{"post":{"operationId":"confirmContactChange","tags":["me"],"summary":"Finish changing your email address or phone number, with the code sent to it","description":"Needs a signed-in user: a session cookie in a browser, `Authorization: Bearer` in the apps. It is about the caller only. From the session that asked for the change, within ten minutes. The new value replaces the old one, proved; a notice goes to the old one (N-14). Five wrong codes void it, and the change starts again.","requestBody":{"required":true,"content":{"application/json":{"schema":{"$ref":"#/components/schemas/ConfirmContactChange"}}}},"responses":{"200":{"description":"The profile, with the new email address or phone number.","content":{"application/json":{"schema":{"$ref":"#/components/schemas/MyProfile"}}}},"400":{"description":"The code is wrong, or there is no change waiting on one any more. Codes: otp_invalid, otp_expired, validation_failed.","content":{"application/problem+json":{"schema":{"$ref":"#/components/schemas/Problem"}}}},"401":{"description":"Not signed in. Codes: unauthenticated.","content":{"application/problem+json":{"schema":{"$ref":"#/components/schemas/Problem"}}}},"409":{"description":"The address or number, proved yours, is on another account. Codes: contact_in_use.","content":{"application/problem+json":{"schema":{"$ref":"#/components/schemas/Problem"}}}},"429":{"description":"Too many requests. Codes: rate_limited.","content":{"application/problem+json":{"schema":{"$ref":"#/components/schemas/Problem"}}}},"500":{"description":"Something went wrong. Codes: internal_error.","content":{"application/problem+json":{"schema":{"$ref":"#/components/schemas/Problem"}}}}}}},"/v1/me/sessions":{"get":{"operationId":"listMySessions","tags":["me"],"summary":"Where you are signed in","description":"Needs a signed-in user: a session cookie in a browser, `Authorization: Bearer` in the apps. It is about the caller only. Every session that has not expired, newest first, with the device, the address and how it was signed in. Read from the live session, never the cookie cache.","responses":{"200":{"description":"Your sessions.","content":{"application/json":{"schema":{"$ref":"#/components/schemas/SignedInSessionList"}}}},"401":{"description":"Not signed in. Codes: unauthenticated.","content":{"application/problem+json":{"schema":{"$ref":"#/components/schemas/Problem"}}}},"429":{"description":"Too many requests. Codes: rate_limited.","content":{"application/problem+json":{"schema":{"$ref":"#/components/schemas/Problem"}}}},"500":{"description":"Something went wrong. Codes: internal_error.","content":{"application/problem+json":{"schema":{"$ref":"#/components/schemas/Problem"}}}}}}},"/v1/me/sessions/{sessionId}":{"delete":{"operationId":"revokeMySession","tags":["me"],"summary":"End one of your sessions","description":"Needs a signed-in user: a session cookie in a browser, `Authorization: Bearer` in the apps. It is about the caller only. The session stops working at once: its next request is `401`. Ending the one this request is made with signs you out here.","parameters":[{"schema":{"$ref":"#/components/schemas/AuthSessionId"},"required":true,"description":"A TypeID: \"aus_\" followed by the base32 form of a UUIDv7.","name":"sessionId","in":"path"}],"responses":{"204":{"description":"Ended."},"400":{"description":"The request could not be understood. Codes: validation_failed.","content":{"application/problem+json":{"schema":{"$ref":"#/components/schemas/Problem"}}}},"401":{"description":"Not signed in. Codes: unauthenticated.","content":{"application/problem+json":{"schema":{"$ref":"#/components/schemas/Problem"}}}},"404":{"description":"No such session of yours. Codes: not_found.","content":{"application/problem+json":{"schema":{"$ref":"#/components/schemas/Problem"}}}},"429":{"description":"Too many requests. Codes: rate_limited.","content":{"application/problem+json":{"schema":{"$ref":"#/components/schemas/Problem"}}}},"500":{"description":"Something went wrong. Codes: internal_error.","content":{"application/problem+json":{"schema":{"$ref":"#/components/schemas/Problem"}}}}}}},"/v1/meta":{"get":{"operationId":"getMeta","tags":["meta"],"summary":"The brand configuration and feature flags","description":"Unauthenticated. The brand configuration clients render (spec/03 §12) — identity, visuals with the computed design tokens, hosts, apps, the legal documents in force and defaults — and the platform’s feature flags. Served `no-cache`: a change HQ makes is in the next fetch.","responses":{"200":{"description":"The brand and the flags.","headers":{"Cache-Control":{"schema":{"type":"string"},"required":true}},"content":{"application/json":{"schema":{"$ref":"#/components/schemas/Meta"}}}},"429":{"description":"Too many requests. Codes: rate_limited.","content":{"application/problem+json":{"schema":{"$ref":"#/components/schemas/Problem"}}}},"500":{"description":"Something went wrong. Codes: internal_error.","content":{"application/problem+json":{"schema":{"$ref":"#/components/schemas/Problem"}}}}}}},"/v1/hq/brand":{"get":{"operationId":"getHqBrand","tags":["brand"],"summary":"Read the brand configuration, as HQ edits it","description":"The brand values HQ has set (support contacts, legal documents, design tokens, the kiosk’s default media, the limits on organisation branding), the deployment’s value of each, the messaging identity, and the brand clients get. Requires `hq:settings`.","x-clavian-permission":"hq:settings","responses":{"200":{"description":"The brand configuration.","headers":{"ETag":{"schema":{"type":"string","description":"The version to send back as If-Match."},"required":true,"description":"The version to send back as If-Match."}},"content":{"application/json":{"schema":{"$ref":"#/components/schemas/HqBrandView"}}}},"401":{"description":"Not signed in. Codes: unauthenticated.","content":{"application/problem+json":{"schema":{"$ref":"#/components/schemas/Problem"}}}},"403":{"description":"Not allowed. Codes: forbidden, second_factor_required.","content":{"application/problem+json":{"schema":{"$ref":"#/components/schemas/Problem"}}}},"429":{"description":"Too many requests. Codes: rate_limited.","content":{"application/problem+json":{"schema":{"$ref":"#/components/schemas/Problem"}}}},"500":{"description":"Something went wrong. Codes: internal_error.","content":{"application/problem+json":{"schema":{"$ref":"#/components/schemas/Problem"}}}}}},"put":{"operationId":"putHqBrand","tags":["brand"],"summary":"Change the brand values HQ sets","description":"Sets a group, or hands it back to the deployment with null; groups left out are unchanged. A change is a new revision, and each changed group is one audit row. A colour below WCAG AA on the chosen surface is refused with the pair named. Send the `ETag` you read as `If-Match`. Requires `hq:settings`.","x-clavian-permission":"hq:settings","parameters":[{"schema":{"type":"string","minLength":1,"maxLength":64,"description":"The ETag this write is based on."},"required":true,"description":"The ETag this write is based on.","name":"if-match","in":"header"}],"requestBody":{"required":true,"content":{"application/json":{"schema":{"$ref":"#/components/schemas/BrandRuntimeChange"}}}},"responses":{"200":{"description":"The brand configuration as it is now.","headers":{"ETag":{"schema":{"type":"string","description":"The version to send back as If-Match."},"required":true,"description":"The version to send back as If-Match."}},"content":{"application/json":{"schema":{"$ref":"#/components/schemas/HqBrandView"}}}},"400":{"description":"The change is not a brand value the schema accepts, or a colour fails WCAG AA. Codes: validation_failed.","content":{"application/problem+json":{"schema":{"$ref":"#/components/schemas/Problem"}}}},"401":{"description":"Not signed in. Codes: unauthenticated.","content":{"application/problem+json":{"schema":{"$ref":"#/components/schemas/Problem"}}}},"403":{"description":"Not allowed. Codes: forbidden, second_factor_required.","content":{"application/problem+json":{"schema":{"$ref":"#/components/schemas/Problem"}}}},"412":{"description":"Someone changed the brand since you read it. Codes: stale_write.","content":{"application/problem+json":{"schema":{"$ref":"#/components/schemas/Problem"}}}},"429":{"description":"Too many requests. Codes: rate_limited.","content":{"application/problem+json":{"schema":{"$ref":"#/components/schemas/Problem"}}}},"500":{"description":"Something went wrong. Codes: internal_error.","content":{"application/problem+json":{"schema":{"$ref":"#/components/schemas/Problem"}}}}}}},"/v1/realtime/token":{"post":{"operationId":"createRealtimeToken","tags":["realtime"],"summary":"A realtime token for the socket: as a member of an organisation, or as the player","description":"Needs a signed-in user: a session cookie in a browser, `Authorization: Bearer` in the apps. The token goes in the first frame on the socket, never in the URL (spec/09 §1). An organisation you are not an active member of is `404`; a role that needs a second factor this session lacks is `403`.","requestBody":{"required":true,"content":{"application/json":{"schema":{"$ref":"#/components/schemas/RealtimeTokenRequest"}}}},"responses":{"200":{"description":"The realtime token.","content":{"application/json":{"schema":{"$ref":"#/components/schemas/RealtimeToken"}}}},"400":{"description":"The request could not be understood. Codes: validation_failed.","content":{"application/problem+json":{"schema":{"$ref":"#/components/schemas/Problem"}}}},"401":{"description":"Not signed in. Codes: unauthenticated.","content":{"application/problem+json":{"schema":{"$ref":"#/components/schemas/Problem"}}}},"403":{"description":"The role needs a second factor this session does not have. Codes: second_factor_required.","content":{"application/problem+json":{"schema":{"$ref":"#/components/schemas/Problem"}}}},"404":{"description":"No such organisation, or not one you are a member of. Codes: not_found.","content":{"application/problem+json":{"schema":{"$ref":"#/components/schemas/Problem"}}}},"429":{"description":"Too many requests. Codes: rate_limited.","content":{"application/problem+json":{"schema":{"$ref":"#/components/schemas/Problem"}}}},"500":{"description":"Something went wrong. Codes: internal_error.","content":{"application/problem+json":{"schema":{"$ref":"#/components/schemas/Problem"}}}}}}},"/v1/auth/two-factor/enable":{"post":{"operationId":"enableTwoFactor","tags":["identity"],"summary":"Start setting up an authenticator app","description":"Needs a session: `Authorization: Bearer <token>` in the apps, the session cookie in a browser. Returns the secret as an `otpauth://` URI and ten backup codes. Nothing is enforced until the first code is verified at `/two-factor/verify-totp`. An account with a password must give it; one without must have signed in within the last ten minutes. With a second factor already on, the session must have completed it.","requestBody":{"required":true,"content":{"application/json":{"schema":{"$ref":"#/components/schemas/TwoFactorEnableRequest"}}}},"responses":{"200":{"description":"The authenticator to set up.","content":{"application/json":{"schema":{"$ref":"#/components/schemas/TwoFactorEnrolment"}}}},"400":{"description":"The password is wrong, or an authenticator is already set up. Codes: INVALID_PASSWORD, TOTP_ALREADY_ENABLED.","content":{"application/json":{"schema":{"$ref":"#/components/schemas/AuthError"}}}},"401":{"description":"Not signed in. Codes: UNAUTHORIZED.","content":{"application/json":{"schema":{"$ref":"#/components/schemas/AuthError"}}}},"403":{"description":"The change is not proven. Codes: SECOND_FACTOR_REQUIRED, SESSION_NOT_FRESH.","content":{"application/json":{"schema":{"$ref":"#/components/schemas/AuthError"}}}}}}},"/v1/auth/two-factor/verify-totp":{"post":{"operationId":"verifyTwoFactorTotp","tags":["identity"],"summary":"Prove the second factor with an authenticator code","description":"Three uses: confirming a new authenticator; finishing a password sign-in that stopped at the second factor (with the `two_factor` cookie that sign-in set); and **stepping up** a session that has only a first factor — a phone code, say — which is what the console routes ask for (`second_factor_required`). The session that comes back records the factor. Five attempts per five minutes per account.","requestBody":{"required":true,"content":{"application/json":{"schema":{"$ref":"#/components/schemas/TwoFactorCodeRequest"}}}},"responses":{"200":{"description":"Proven.","content":{"application/json":{"schema":{"$ref":"#/components/schemas/TwoFactorVerified"}}}},"400":{"description":"No authenticator is set up. Codes: TOTP_NOT_ENABLED.","content":{"application/json":{"schema":{"$ref":"#/components/schemas/AuthError"}}}},"401":{"description":"The code is wrong, or there is no session or sign-in to prove it for. Codes: INVALID_CODE, INVALID_TWO_FACTOR_COOKIE.","content":{"application/json":{"schema":{"$ref":"#/components/schemas/AuthError"}}}},"403":{"description":"Banned from the Platform: the credential was right, and no session is created. \"This account cannot sign in. Please contact support.\" — never the reason. Codes: BANNED_USER.","content":{"application/json":{"schema":{"$ref":"#/components/schemas/AuthError"}}}},"429":{"description":"Five attempts in five minutes for this account. Codes: RATE_LIMITED.","content":{"application/json":{"schema":{"$ref":"#/components/schemas/AuthError"}}}}}}},"/v1/auth/two-factor/verify-backup-code":{"post":{"operationId":"verifyTwoFactorBackupCode","tags":["identity"],"summary":"Prove the second factor with a backup code","description":"As `/two-factor/verify-totp`, with one of the backup codes instead. Each code works once and is then spent. Counts towards the same five attempts per five minutes per account.","requestBody":{"required":true,"content":{"application/json":{"schema":{"$ref":"#/components/schemas/TwoFactorCodeRequest"}}}},"responses":{"200":{"description":"Proven; the code is spent.","content":{"application/json":{"schema":{"$ref":"#/components/schemas/TwoFactorVerified"}}}},"400":{"description":"No backup codes are set up. Codes: BACKUP_CODES_NOT_ENABLED.","content":{"application/json":{"schema":{"$ref":"#/components/schemas/AuthError"}}}},"401":{"description":"The code is wrong or already spent. Codes: INVALID_BACKUP_CODE, INVALID_TWO_FACTOR_COOKIE.","content":{"application/json":{"schema":{"$ref":"#/components/schemas/AuthError"}}}},"403":{"description":"Banned from the Platform: the credential was right, and no session is created. \"This account cannot sign in. Please contact support.\" — never the reason. Codes: BANNED_USER.","content":{"application/json":{"schema":{"$ref":"#/components/schemas/AuthError"}}}},"429":{"description":"Five attempts in five minutes for this account. Codes: RATE_LIMITED.","content":{"application/json":{"schema":{"$ref":"#/components/schemas/AuthError"}}}}}}},"/v1/auth/two-factor/generate-backup-codes":{"post":{"operationId":"generateTwoFactorBackupCodes","tags":["identity"],"summary":"Replace the backup codes","description":"Needs a session: `Authorization: Bearer <token>` in the apps, the session cookie in a browser. The session must have completed the second factor. The old codes stop working.","requestBody":{"required":true,"content":{"application/json":{"schema":{"$ref":"#/components/schemas/TwoFactorPasswordRequest"}}}},"responses":{"200":{"description":"The new codes.","content":{"application/json":{"schema":{"$ref":"#/components/schemas/TwoFactorBackupCodes"}}}},"400":{"description":"The password is wrong. Codes: INVALID_PASSWORD.","content":{"application/json":{"schema":{"$ref":"#/components/schemas/AuthError"}}}},"401":{"description":"Not signed in. Codes: UNAUTHORIZED.","content":{"application/json":{"schema":{"$ref":"#/components/schemas/AuthError"}}}},"403":{"description":"The session has not completed the second factor. Codes: SECOND_FACTOR_REQUIRED.","content":{"application/json":{"schema":{"$ref":"#/components/schemas/AuthError"}}}}}}},"/v1/auth/two-factor/disable":{"post":{"operationId":"disableTwoFactor","tags":["identity"],"summary":"Turn the second factor off","description":"Needs a session: `Authorization: Bearer <token>` in the apps, the session cookie in a browser. The session must have completed the second factor. A staff member whose role requires one (`staff.require2faRoles`) is refused at the console until they set one up again.","requestBody":{"required":true,"content":{"application/json":{"schema":{"$ref":"#/components/schemas/TwoFactorPasswordRequest"}}}},"responses":{"200":{"description":"Off.","content":{"application/json":{"schema":{"$ref":"#/components/schemas/TwoFactorStatus"}}}},"400":{"description":"The password is wrong. Codes: INVALID_PASSWORD.","content":{"application/json":{"schema":{"$ref":"#/components/schemas/AuthError"}}}},"401":{"description":"Not signed in. Codes: UNAUTHORIZED.","content":{"application/json":{"schema":{"$ref":"#/components/schemas/AuthError"}}}},"403":{"description":"The session has not completed the second factor. Codes: SECOND_FACTOR_REQUIRED.","content":{"application/json":{"schema":{"$ref":"#/components/schemas/AuthError"}}}}}}},"/v1/auth/phone-number/send-otp":{"post":{"operationId":"sendPhoneNumberOTP","tags":["identity"],"summary":"Send a sign-in code to a phone","description":"A 6-digit code, valid for 10 minutes, by SMS or WhatsApp (`auth.otpChannel`). A kiosk never calls this: it uses `/v1/station/sign-in/phone/code`. A 503 means no code was sent — never that one was.","requestBody":{"required":true,"content":{"application/json":{"schema":{"$ref":"#/components/schemas/PhoneCodeRequest"}}}},"responses":{"200":{"description":"The code has been handed to a provider.","content":{"application/json":{"schema":{"$ref":"#/components/schemas/AuthMessage"}}}},"400":{"description":"The number cannot be used. Codes: INVALID_PHONE_NUMBER, PREMIUM_RATE_NUMBER.","content":{"application/json":{"schema":{"$ref":"#/components/schemas/AuthError"}}}},"429":{"description":"Too many codes: three per ten minutes and ten a day for one number, twenty an hour from one address, or the account is locked. `Retry-After` says when to try again. Codes: RATE_LIMITED.","content":{"application/json":{"schema":{"$ref":"#/components/schemas/AuthError"}}}},"503":{"description":"No provider could send the code; nothing was sent. Codes: OTP_DELIVERY_UNAVAILABLE.","content":{"application/json":{"schema":{"$ref":"#/components/schemas/AuthError"}}}}}}},"/v1/auth/phone-number/verify":{"post":{"operationId":"verifyPhoneNumber","tags":["identity"],"summary":"Sign in, or sign up, with the code sent to a phone","description":"Creates the account on the first verification of a new number — only with the terms and privacy accepted in `consent`. Five wrong codes void the code; five failures in fifteen minutes lock the account for fifteen.","requestBody":{"required":true,"content":{"application/json":{"schema":{"$ref":"#/components/schemas/VerifyPhoneNumberRequest"}}}},"responses":{"200":{"description":"Signed in.","content":{"application/json":{"schema":{"$ref":"#/components/schemas/PhoneVerified"}}}},"400":{"description":"The code is wrong, expired or used up, a field is not accepted, or a new account was asked for without the terms and privacy. Codes: INVALID_OTP, OTP_EXPIRED, OTP_NOT_FOUND, TOO_MANY_ATTEMPTS, INVALID_PHONE_NUMBER, UNEXPECTED_FIELDS, CONSENT_REQUIRED.","content":{"application/json":{"schema":{"$ref":"#/components/schemas/AuthError"}}}},"403":{"description":"Banned from the Platform: the credential was right, and no session is created. \"This account cannot sign in. Please contact support.\" — never the reason. Codes: BANNED_USER.","content":{"application/json":{"schema":{"$ref":"#/components/schemas/AuthError"}}}},"409":{"description":"The consent names a document version that is not the one in force. Codes: CONSENT_VERSION_OUTDATED.","content":{"application/json":{"schema":{"$ref":"#/components/schemas/AuthError"}}}},"429":{"description":"Five failures in fifteen minutes on this account, which is locked for fifteen. `Retry-After` says when to try again. Codes: RATE_LIMITED.","content":{"application/json":{"schema":{"$ref":"#/components/schemas/AuthError"}}}}}}},"/v1/auth/phone-number/request-password-reset":{"post":{"operationId":"requestPasswordResetPhoneNumber","tags":["identity"],"summary":"Send a code to set or reset the password","description":"Also how a phone-first player **adds** a password. The same answer, taking no less than 500 ms, whether or not the number has an account; at most three codes an hour reach one account.","requestBody":{"required":true,"content":{"application/json":{"schema":{"$ref":"#/components/schemas/PhoneCodeRequest"}}}},"responses":{"200":{"description":"Accepted.","content":{"application/json":{"schema":{"$ref":"#/components/schemas/AuthStatus"}}}},"429":{"description":"Too many attempts. `Retry-After` says when to try again. Codes: RATE_LIMITED.","content":{"application/json":{"schema":{"$ref":"#/components/schemas/AuthError"}}}}}}},"/v1/auth/phone-number/reset-password":{"post":{"operationId":"resetPasswordPhoneNumber","tags":["identity"],"summary":"Set a new password with the code sent to the phone","description":"Creates the password when the account has none. The new password is checked against breached-password lists.","requestBody":{"required":true,"content":{"application/json":{"schema":{"$ref":"#/components/schemas/ResetPasswordWithPhoneCodeRequest"}}}},"responses":{"200":{"description":"The password is set.","content":{"application/json":{"schema":{"$ref":"#/components/schemas/AuthStatus"}}}},"400":{"description":"The code is wrong, expired or used up, or the password is refused. Codes: INVALID_OTP, OTP_EXPIRED, OTP_NOT_FOUND, TOO_MANY_ATTEMPTS, PASSWORD_TOO_SHORT, PASSWORD_TOO_LONG, PASSWORD_COMPROMISED.","content":{"application/json":{"schema":{"$ref":"#/components/schemas/AuthError"}}}},"429":{"description":"Too many attempts. `Retry-After` says when to try again. Codes: RATE_LIMITED.","content":{"application/json":{"schema":{"$ref":"#/components/schemas/AuthError"}}}}}}},"/v1/auth/sign-up/email":{"post":{"operationId":"signUpWithEmailAndPassword","tags":["identity"],"summary":"Create an account with an email address and a password","description":"Needs the terms and privacy accepted in `consent`. The account cannot sign in until its address is verified with the code sent to it (`/email-otp/verify-email`). The password is checked against breached-password lists.","requestBody":{"required":true,"content":{"application/json":{"schema":{"$ref":"#/components/schemas/SignUpWithEmailRequest"}}}},"responses":{"200":{"description":"Accepted.","content":{"application/json":{"schema":{"$ref":"#/components/schemas/SignedUp"}}}},"400":{"description":"Refused. Codes: CONSENT_REQUIRED, INVALID_EMAIL, PASSWORD_TOO_SHORT, PASSWORD_TOO_LONG, PASSWORD_COMPROMISED, USERNAME_IS_ALREADY_TAKEN (with suggestions), USERNAME_TOO_SHORT, USERNAME_TOO_LONG, INVALID_USERNAME.","content":{"application/json":{"schema":{"$ref":"#/components/schemas/AuthErrorWithSuggestions"}}}},"409":{"description":"The consent names a document version that is not the one in force. Codes: CONSENT_VERSION_OUTDATED.","content":{"application/json":{"schema":{"$ref":"#/components/schemas/AuthError"}}}},"429":{"description":"Too many attempts. `Retry-After` says when to try again. Codes: RATE_LIMITED.","content":{"application/json":{"schema":{"$ref":"#/components/schemas/AuthError"}}}}}}},"/v1/auth/email-otp/send-verification-otp":{"post":{"operationId":"sendEmailVerificationOTP","tags":["identity"],"summary":"Email a one-time code","description":"A 6-digit code, valid for 10 minutes, five attempts. The answer is the same whether or not the address has an account, and whether or not the code could be sent; a synthetic `.invalid` address is refused.","requestBody":{"required":true,"content":{"application/json":{"schema":{"$ref":"#/components/schemas/SendEmailCodeRequest"}}}},"responses":{"200":{"description":"Accepted.","content":{"application/json":{"schema":{"$ref":"#/components/schemas/AuthSuccess"}}}},"400":{"description":"The address cannot be used. Codes: INVALID_EMAIL.","content":{"application/json":{"schema":{"$ref":"#/components/schemas/AuthError"}}}},"429":{"description":"Too many attempts. `Retry-After` says when to try again. Codes: RATE_LIMITED.","content":{"application/json":{"schema":{"$ref":"#/components/schemas/AuthError"}}}}}}},"/v1/auth/email-otp/verify-email":{"post":{"operationId":"verifyEmailOTP","tags":["identity"],"summary":"Verify an email address with the code sent to it","requestBody":{"required":true,"content":{"application/json":{"schema":{"$ref":"#/components/schemas/EmailCodeRequest"}}}},"responses":{"200":{"description":"Verified.","content":{"application/json":{"schema":{"$ref":"#/components/schemas/EmailVerified"}}}},"400":{"description":"The code is wrong, expired or used up. Codes: INVALID_OTP, OTP_EXPIRED, TOO_MANY_ATTEMPTS, INVALID_EMAIL.","content":{"application/json":{"schema":{"$ref":"#/components/schemas/AuthError"}}}},"429":{"description":"Too many attempts. `Retry-After` says when to try again. Codes: RATE_LIMITED.","content":{"application/json":{"schema":{"$ref":"#/components/schemas/AuthError"}}}}}}},"/v1/auth/sign-in/email":{"post":{"operationId":"signInEmail","tags":["identity"],"summary":"Sign in with an email address and a password","description":"The address must be verified. An account with a second factor stops at `twoFactorRedirect`.","requestBody":{"required":true,"content":{"application/json":{"schema":{"$ref":"#/components/schemas/SignInWithEmailRequest"}}}},"responses":{"200":{"description":"Signed in, or waiting for the second factor.","content":{"application/json":{"schema":{"$ref":"#/components/schemas/PasswordSignIn"}}}},"401":{"description":"The password is wrong, or there is no such account — the same answer, at the same time, either way. Codes: INVALID_EMAIL_OR_PASSWORD, INVALID_USERNAME_OR_PASSWORD.","content":{"application/json":{"schema":{"$ref":"#/components/schemas/AuthError"}}}},"403":{"description":"The right password, for an account with no verified email or phone to sign in by; or an account banned from the Platform, which is refused every session and never told why (spec/07 §3.5). Codes: EMAIL_NOT_VERIFIED, BANNED_USER.","content":{"application/json":{"schema":{"$ref":"#/components/schemas/AuthError"}}}},"429":{"description":"Too many attempts: ten a minute from one address, or five failures in fifteen minutes on one account, which then locks it for fifteen minutes. `Retry-After` says when to try again. Codes: RATE_LIMITED.","content":{"application/json":{"schema":{"$ref":"#/components/schemas/AuthError"}}}}}}},"/v1/auth/sign-in/username":{"post":{"operationId":"signInUsername","tags":["identity"],"summary":"Sign in with a gamer tag and a password","description":"The account needs a verified email or a verified phone, so a phone-first player who has added a password can use it. An account with a second factor stops at `twoFactorRedirect`.","requestBody":{"required":true,"content":{"application/json":{"schema":{"$ref":"#/components/schemas/SignInWithUsernameRequest"}}}},"responses":{"200":{"description":"Signed in, or waiting for the second factor.","content":{"application/json":{"schema":{"$ref":"#/components/schemas/PasswordSignIn"}}}},"401":{"description":"The password is wrong, or there is no such account — the same answer, at the same time, either way. Codes: INVALID_EMAIL_OR_PASSWORD, INVALID_USERNAME_OR_PASSWORD.","content":{"application/json":{"schema":{"$ref":"#/components/schemas/AuthError"}}}},"403":{"description":"The right password, for an account with no verified email or phone to sign in by; or an account banned from the Platform, which is refused every session and never told why (spec/07 §3.5). Codes: EMAIL_NOT_VERIFIED, BANNED_USER.","content":{"application/json":{"schema":{"$ref":"#/components/schemas/AuthError"}}}},"429":{"description":"Too many attempts: ten a minute from one address, or five failures in fifteen minutes on one account, which then locks it for fifteen minutes. `Retry-After` says when to try again. Codes: RATE_LIMITED.","content":{"application/json":{"schema":{"$ref":"#/components/schemas/AuthError"}}}}}}},"/v1/auth/sign-in/email-otp":{"post":{"operationId":"signInWithEmailOTP","tags":["identity"],"summary":"Sign in with a code emailed to the address","description":"Passwordless sign-in on the web. It signs an existing account in and never creates one.","requestBody":{"required":true,"content":{"application/json":{"schema":{"$ref":"#/components/schemas/EmailCodeRequest"}}}},"responses":{"200":{"description":"Signed in.","content":{"application/json":{"schema":{"$ref":"#/components/schemas/SignedIn"}}}},"400":{"description":"The code is wrong, expired or used up. Codes: INVALID_OTP, OTP_EXPIRED, TOO_MANY_ATTEMPTS, INVALID_EMAIL.","content":{"application/json":{"schema":{"$ref":"#/components/schemas/AuthError"}}}},"403":{"description":"Banned from the Platform: the credential was right, and no session is created. \"This account cannot sign in. Please contact support.\" — never the reason. Codes: BANNED_USER.","content":{"application/json":{"schema":{"$ref":"#/components/schemas/AuthError"}}}},"429":{"description":"Too many attempts. `Retry-After` says when to try again. Codes: RATE_LIMITED.","content":{"application/json":{"schema":{"$ref":"#/components/schemas/AuthError"}}}}}}},"/v1/auth/email-otp/request-password-reset":{"post":{"operationId":"requestPasswordResetWithEmailOTP","tags":["identity"],"summary":"Email a code to reset the password","description":"The same answer, taking no less than 500 ms, whether or not the address has an account (spec/07 §2.2); at most three codes an hour reach one account.","requestBody":{"required":true,"content":{"application/json":{"schema":{"$ref":"#/components/schemas/EmailPasswordResetRequest"}}}},"responses":{"200":{"description":"Accepted.","content":{"application/json":{"schema":{"$ref":"#/components/schemas/AuthSuccess"}}}},"400":{"description":"The address cannot be used. Codes: INVALID_EMAIL.","content":{"application/json":{"schema":{"$ref":"#/components/schemas/AuthError"}}}},"429":{"description":"Too many attempts. `Retry-After` says when to try again. Codes: RATE_LIMITED.","content":{"application/json":{"schema":{"$ref":"#/components/schemas/AuthError"}}}}}}},"/v1/auth/email-otp/reset-password":{"post":{"operationId":"resetPasswordWithEmailOTP","tags":["identity"],"summary":"Set a new password with the code emailed to the address","description":"The new password is checked against breached-password lists.","requestBody":{"required":true,"content":{"application/json":{"schema":{"$ref":"#/components/schemas/ResetPasswordWithEmailCodeRequest"}}}},"responses":{"200":{"description":"The password is changed.","content":{"application/json":{"schema":{"$ref":"#/components/schemas/AuthSuccess"}}}},"400":{"description":"The code is wrong, expired or used up, or the password is refused. Codes: INVALID_OTP, OTP_EXPIRED, TOO_MANY_ATTEMPTS, PASSWORD_TOO_SHORT, PASSWORD_TOO_LONG, PASSWORD_COMPROMISED.","content":{"application/json":{"schema":{"$ref":"#/components/schemas/AuthError"}}}},"429":{"description":"Too many attempts. `Retry-After` says when to try again. Codes: RATE_LIMITED.","content":{"application/json":{"schema":{"$ref":"#/components/schemas/AuthError"}}}}}}},"/v1/auth/is-username-available":{"post":{"operationId":"isUsernameAvailable","tags":["identity"],"summary":"Is this gamer tag free?","description":"Unique ignoring case. A taken tag comes back with alternatives.","requestBody":{"required":true,"content":{"application/json":{"schema":{"$ref":"#/components/schemas/UsernameAvailabilityRequest"}}}},"responses":{"200":{"description":"Whether it is free.","content":{"application/json":{"schema":{"$ref":"#/components/schemas/UsernameAvailability"}}}},"422":{"description":"Not a gamer tag: 3–20 letters, digits, \"_\" or \".\". Codes: USERNAME_TOO_SHORT, USERNAME_TOO_LONG, INVALID_USERNAME.","content":{"application/json":{"schema":{"$ref":"#/components/schemas/AuthError"}}}},"429":{"description":"Too many attempts. `Retry-After` says when to try again. Codes: RATE_LIMITED.","content":{"application/json":{"schema":{"$ref":"#/components/schemas/AuthError"}}}}}}},"/v1/auth/update-user":{"post":{"operationId":"updateUser","tags":["identity"],"summary":"Choose a gamer tag","description":"Needs a session: `Authorization: Bearer <token>` in the apps, the session cookie in a browser. A tag somebody else holds, in any capitalisation, is refused with alternatives.","requestBody":{"required":true,"content":{"application/json":{"schema":{"$ref":"#/components/schemas/UpdateUserRequest"}}}},"responses":{"200":{"description":"Chosen.","content":{"application/json":{"schema":{"$ref":"#/components/schemas/AuthStatus"}}}},"400":{"description":"Refused. Codes: USERNAME_IS_ALREADY_TAKEN (with suggestions), USERNAME_TOO_SHORT, USERNAME_TOO_LONG, INVALID_USERNAME, DISPLAY_USERNAME_NOT_ACCEPTED.","content":{"application/json":{"schema":{"$ref":"#/components/schemas/AuthErrorWithSuggestions"}}}},"401":{"description":"Not signed in. Codes: UNAUTHORIZED.","content":{"application/json":{"schema":{"$ref":"#/components/schemas/AuthError"}}}},"429":{"description":"Too many attempts. `Retry-After` says when to try again. Codes: RATE_LIMITED.","content":{"application/json":{"schema":{"$ref":"#/components/schemas/AuthError"}}}}}}},"/v1/auth/sign-in/social":{"post":{"operationId":"socialSignIn","tags":["identity"],"summary":"Sign in, or sign up, with Google or Discord","description":"The browser flow: the answer is the provider’s URL, and the provider sends the player back to `/v1/auth/callback/{provider}`. The phone apps’ Google flow: send the SDK’s `idToken` and get a session back. Send `consent` whenever the player may be new. No account is ever joined to another by email: an address that already has an account is refused (`account_not_linked`), and the provider is linked from that account’s session (`/v1/auth/link-social`). Never offered on a kiosk.","requestBody":{"required":true,"content":{"application/json":{"schema":{"$ref":"#/components/schemas/SocialSignInRequest"}}}},"responses":{"200":{"description":"The provider’s URL, or — with an ID token — a session.","content":{"application/json":{"schema":{"$ref":"#/components/schemas/SocialSignIn"}}}},"400":{"description":"Refused: a field is not accepted, or a new player’s ID token came without the terms and privacy. Codes: UNEXPECTED_FIELDS, CONSENT_REQUIRED, VALIDATION_ERROR.","content":{"application/json":{"schema":{"$ref":"#/components/schemas/AuthError"}}}},"401":{"description":"The ID token is not Google’s, not for this api or expired, or its address already has an account. Codes: INVALID_TOKEN, OAUTH_LINK_ERROR.","content":{"application/json":{"schema":{"$ref":"#/components/schemas/AuthError"}}}},"403":{"description":"The account is banned from the Platform, or a callback URL is not one the api trusts. Codes: BANNED_USER, INVALID_CALLBACK_URL.","content":{"application/json":{"schema":{"$ref":"#/components/schemas/AuthError"}}}},"404":{"description":"The provider is not configured on this server, or cannot take an ID token. Codes: PROVIDER_NOT_FOUND, ID_TOKEN_NOT_SUPPORTED.","content":{"application/json":{"schema":{"$ref":"#/components/schemas/AuthError"}}}},"409":{"description":"The consent names a document version that is not the one in force. Codes: CONSENT_VERSION_OUTDATED.","content":{"application/json":{"schema":{"$ref":"#/components/schemas/AuthError"}}}},"429":{"description":"Too many attempts. `Retry-After` says when to try again. Codes: RATE_LIMITED.","content":{"application/json":{"schema":{"$ref":"#/components/schemas/AuthError"}}}}}}},"/v1/auth/callback/{id}":{"get":{"operationId":"callbackOAuth","tags":["identity"],"summary":"Where Google or Discord sends the player back","description":"Not called by a client: the provider redirects the browser here. The state must be the one this browser started with, unused and under 10 minutes old. A new player’s account, its profile and its consent rows are created here in one transaction, with the consent kept from `/v1/auth/sign-in/social`.","parameters":[{"schema":{"$ref":"#/components/schemas/SocialSignInProvider"},"required":true,"description":"An OAuth provider a player may sign in with.","name":"id","in":"path"},{"schema":{"type":"string"},"required":false,"name":"code","in":"query"},{"schema":{"type":"string"},"required":false,"name":"state","in":"query"},{"schema":{"type":"string"},"required":false,"name":"error","in":"query"},{"schema":{"type":"string"},"required":false,"name":"error_description","in":"query"}],"responses":{"302":{"description":"A redirect. Signed in: to `callbackURL` (or `newUserCallbackURL` for a new player) with the session cookie. Refused: to `errorCallbackURL` with `?error=<code>`, and no session."}}}},"/v1/auth/steam/start":{"post":{"operationId":"steamStart","tags":["identity"],"summary":"Sign in, or sign up, with Steam","description":"The answer is Steam’s sign-in URL, whose `openid.return_to` is `/v1/auth/steam/callback` with a one-use state kept for 10 minutes and bound to this browser; Steam sends the player back there. Send `consent` whenever the player may be new. Never offered on a kiosk.","requestBody":{"required":true,"content":{"application/json":{"schema":{"$ref":"#/components/schemas/SteamStartRequest"}}}},"responses":{"200":{"description":"Steam’s sign-in URL.","content":{"application/json":{"schema":{"$ref":"#/components/schemas/SteamStart"}}}},"400":{"description":"A field is not accepted, or the consent is not one that can be recorded. Codes: UNEXPECTED_FIELDS, CONSENT_REQUIRED, VALIDATION_ERROR.","content":{"application/json":{"schema":{"$ref":"#/components/schemas/AuthError"}}}},"401":{"description":"`link` without a session. Codes: UNAUTHORIZED.","content":{"application/json":{"schema":{"$ref":"#/components/schemas/AuthError"}}}},"403":{"description":"A callback URL is not one the api trusts. Codes: INVALID_CALLBACK_URL.","content":{"application/json":{"schema":{"$ref":"#/components/schemas/AuthError"}}}},"404":{"description":"Steam sign-in is not configured on this server. Codes: PROVIDER_NOT_FOUND.","content":{"application/json":{"schema":{"$ref":"#/components/schemas/AuthError"}}}},"409":{"description":"The consent names a document version that is not the one in force. Codes: CONSENT_VERSION_OUTDATED.","content":{"application/json":{"schema":{"$ref":"#/components/schemas/AuthError"}}}},"429":{"description":"Too many attempts. `Retry-After` says when to try again. Codes: RATE_LIMITED.","content":{"application/json":{"schema":{"$ref":"#/components/schemas/AuthError"}}}}}}},"/v1/auth/steam/callback":{"get":{"operationId":"steamCallback","tags":["identity"],"summary":"Where Steam sends the player back","description":"Not called by a client: Steam redirects the browser here with its OpenID 2.0 assertion (`openid.*`). Nothing is believed until the state is this browser’s and unused, the assertion is Steam’s for the exact URL issued, its nonce is unseen in 24 hours and Steam’s `check_authentication` confirms it (spec/07 §3.4). The account is found by its steamId64, or created with its consent in one transaction.","parameters":[{"schema":{"type":"string"},"required":false,"name":"state","in":"query"}],"responses":{"302":{"description":"A redirect. Signed in: to `callbackURL` (or `newUserCallbackURL` for a new player) with the session cookie. Refused: to `errorCallbackURL` with `?error=<code>`, and no session."}}}},"/v1/auth/link-social":{"post":{"operationId":"linkSocialAccount","tags":["identity"],"summary":"Link Google or Discord to the signed-in account","description":"Needs a session: `Authorization: Bearer <token>` in the apps, the session cookie in a browser. The only way a provider joins an existing account (\"Link Discord\" in settings): the session proves the account and the round trip the identity, so the two addresses need not match. The provider sends the player back to `/v1/auth/callback/{provider}`; a provider account already linked to somebody else is refused.","requestBody":{"required":true,"content":{"application/json":{"schema":{"$ref":"#/components/schemas/LinkSocialAccountRequest"}}}},"responses":{"200":{"description":"The provider’s URL.","content":{"application/json":{"schema":{"$ref":"#/components/schemas/LinkSocialAccount"}}}},"400":{"description":"A field is not accepted. Codes: UNEXPECTED_FIELDS, VALIDATION_ERROR.","content":{"application/json":{"schema":{"$ref":"#/components/schemas/AuthError"}}}},"401":{"description":"Not signed in. Codes: UNAUTHORIZED.","content":{"application/json":{"schema":{"$ref":"#/components/schemas/AuthError"}}}},"404":{"description":"The provider is not configured on this server. Codes: PROVIDER_NOT_FOUND.","content":{"application/json":{"schema":{"$ref":"#/components/schemas/AuthError"}}}},"429":{"description":"Too many attempts. `Retry-After` says when to try again. Codes: RATE_LIMITED.","content":{"application/json":{"schema":{"$ref":"#/components/schemas/AuthError"}}}}}}},"/v1/auth/list-accounts":{"get":{"operationId":"listUserAccounts","tags":["identity"],"summary":"The ways into the signed-in account","description":"Needs a session: `Authorization: Bearer <token>` in the apps, the session cookie in a browser. Its password and every provider linked to it, for the security settings.","responses":{"200":{"description":"Every way in.","content":{"application/json":{"schema":{"type":"array","items":{"$ref":"#/components/schemas/LinkedAccount"}}}}},"401":{"description":"Not signed in. Codes: UNAUTHORIZED.","content":{"application/json":{"schema":{"$ref":"#/components/schemas/AuthError"}}}},"429":{"description":"Too many attempts. `Retry-After` says when to try again. Codes: RATE_LIMITED.","content":{"application/json":{"schema":{"$ref":"#/components/schemas/AuthError"}}}}}}},"/v1/auth/get-session":{"get":{"operationId":"getSession","tags":["identity"],"summary":"The caller’s session","description":"Needs a session: `Authorization: Bearer <token>` in the apps, the session cookie in a browser. Answers `null`, not 401, when there is none.","responses":{"200":{"description":"The session and its user, or `null`.","content":{"application/json":{"schema":{"$ref":"#/components/schemas/AuthSessionState"}}}},"429":{"description":"Too many attempts. `Retry-After` says when to try again. Codes: RATE_LIMITED.","content":{"application/json":{"schema":{"$ref":"#/components/schemas/AuthError"}}}}}}},"/v1/auth/sign-out":{"post":{"operationId":"signOut","tags":["identity"],"summary":"Sign out","description":"Ends the caller’s session. Answers the same when there is none.","responses":{"200":{"description":"Signed out.","content":{"application/json":{"schema":{"$ref":"#/components/schemas/AuthSuccess"}}}},"429":{"description":"Too many attempts. `Retry-After` says when to try again. Codes: RATE_LIMITED.","content":{"application/json":{"schema":{"$ref":"#/components/schemas/AuthError"}}}}}}},"/v1/organizations/{organizationId}/players":{"get":{"operationId":"listPlayers","tags":["players"],"summary":"Search the organisation’s players","description":"This organisation’s players only — the ones it has dealt with — by name, username, phone, email, member QR or tag, oldest first. Contact details are masked without `players:read_contact`. Requires `players:read`.","x-clavian-permission":"players:read","x-clavian-unmasked-by":"players:read_contact","parameters":[{"schema":{"$ref":"#/components/schemas/OrganizationId"},"required":true,"description":"A TypeID: \"org_\" followed by the base32 form of a UUIDv7.","name":"organizationId","in":"path"},{"schema":{"type":"string","minLength":1,"description":"The `page.cursor` from the previous response."},"required":false,"description":"The `page.cursor` from the previous response.","name":"cursor","in":"query"},{"schema":{"type":"integer","minimum":1,"maximum":100,"default":25,"description":"Items per page, 1–100."},"required":false,"description":"Items per page, 1–100.","name":"limit","in":"query"},{"schema":{"type":"string","minLength":2,"maxLength":100,"description":"A phone number in E.164 (`+919876543210`) or an email address, matched exactly; anything else is matched against the name, display name and username. Only among this organisation’s players."},"required":false,"description":"A phone number in E.164 (`+919876543210`) or an email address, matched exactly; anything else is matched against the name, display name and username. Only among this organisation’s players.","name":"q","in":"query"},{"schema":{"allOf":[{"$ref":"#/components/schemas/PlayerId"},{"description":"What the player’s member QR says — their player id — as the scanner read it."}]},"required":false,"description":"What the player’s member QR says — their player id — as the scanner read it.","name":"memberQr","in":"query"},{"schema":{"$ref":"#/components/schemas/PlayerTag"},"required":false,"description":"Only players with this tag: a list, or an audience.","name":"tag","in":"query"}],"responses":{"200":{"description":"A page of players.","content":{"application/json":{"schema":{"$ref":"#/components/schemas/OrganizationPlayerList"}}}},"400":{"description":"The request could not be understood. Codes: validation_failed.","content":{"application/problem+json":{"schema":{"$ref":"#/components/schemas/Problem"}}}},"401":{"description":"Not signed in. Codes: unauthenticated.","content":{"application/problem+json":{"schema":{"$ref":"#/components/schemas/Problem"}}}},"403":{"description":"Your role does not allow this. Codes: forbidden, second_factor_required.","content":{"application/problem+json":{"schema":{"$ref":"#/components/schemas/Problem"}}}},"404":{"description":"No such organisation, or no such player among its players — a player it has never dealt with included. Codes: not_found.","content":{"application/problem+json":{"schema":{"$ref":"#/components/schemas/Problem"}}}},"429":{"description":"Too many requests. Codes: rate_limited.","content":{"application/problem+json":{"schema":{"$ref":"#/components/schemas/Problem"}}}},"500":{"description":"Something went wrong. Codes: internal_error.","content":{"application/problem+json":{"schema":{"$ref":"#/components/schemas/Problem"}}}}}}},"/v1/organizations/{organizationId}/players/{playerId}":{"get":{"operationId":"getPlayer","tags":["players"],"summary":"One player, as this organisation sees them","description":"Profile basics, this organisation’s tags and notes. A player it has never dealt with is `404`. Contact details are masked without `players:read_contact`. Requires `players:read`.","x-clavian-permission":"players:read","x-clavian-unmasked-by":"players:read_contact","parameters":[{"schema":{"$ref":"#/components/schemas/OrganizationId"},"required":true,"description":"A TypeID: \"org_\" followed by the base32 form of a UUIDv7.","name":"organizationId","in":"path"},{"schema":{"$ref":"#/components/schemas/PlayerId"},"required":true,"description":"A TypeID: \"ply_\" followed by the base32 form of a UUIDv7.","name":"playerId","in":"path"}],"responses":{"200":{"description":"The player.","content":{"application/json":{"schema":{"$ref":"#/components/schemas/OrganizationPlayerProfile"}}}},"400":{"description":"The request could not be understood. Codes: validation_failed.","content":{"application/problem+json":{"schema":{"$ref":"#/components/schemas/Problem"}}}},"401":{"description":"Not signed in. Codes: unauthenticated.","content":{"application/problem+json":{"schema":{"$ref":"#/components/schemas/Problem"}}}},"403":{"description":"Your role does not allow this. Codes: forbidden, second_factor_required.","content":{"application/problem+json":{"schema":{"$ref":"#/components/schemas/Problem"}}}},"404":{"description":"No such organisation, or no such player among its players — a player it has never dealt with included. Codes: not_found.","content":{"application/problem+json":{"schema":{"$ref":"#/components/schemas/Problem"}}}},"429":{"description":"Too many requests. Codes: rate_limited.","content":{"application/problem+json":{"schema":{"$ref":"#/components/schemas/Problem"}}}},"500":{"description":"Something went wrong. Codes: internal_error.","content":{"application/problem+json":{"schema":{"$ref":"#/components/schemas/Problem"}}}}}},"patch":{"operationId":"updatePlayer","tags":["players"],"summary":"Add, edit or remove notes and tags","description":"Every note added, edited or removed writes one audit row, with the text masked; a change to the tags writes one with the tags before and after. All of it or none of it. Requires `players:notes`.","x-clavian-permission":"players:notes","x-clavian-unmasked-by":"players:read_contact","parameters":[{"schema":{"$ref":"#/components/schemas/OrganizationId"},"required":true,"description":"A TypeID: \"org_\" followed by the base32 form of a UUIDv7.","name":"organizationId","in":"path"},{"schema":{"$ref":"#/components/schemas/PlayerId"},"required":true,"description":"A TypeID: \"ply_\" followed by the base32 form of a UUIDv7.","name":"playerId","in":"path"}],"requestBody":{"required":true,"content":{"application/json":{"schema":{"$ref":"#/components/schemas/UpdateOrganizationPlayer"}}}},"responses":{"200":{"description":"The player, with the change.","content":{"application/json":{"schema":{"$ref":"#/components/schemas/OrganizationPlayerProfile"}}}},"400":{"description":"The request could not be understood. Codes: validation_failed.","content":{"application/problem+json":{"schema":{"$ref":"#/components/schemas/Problem"}}}},"401":{"description":"Not signed in. Codes: unauthenticated.","content":{"application/problem+json":{"schema":{"$ref":"#/components/schemas/Problem"}}}},"403":{"description":"Your role does not allow this. Codes: forbidden, second_factor_required.","content":{"application/problem+json":{"schema":{"$ref":"#/components/schemas/Problem"}}}},"404":{"description":"No such player among the organisation’s players, or no such note of theirs. Codes: not_found.","content":{"application/problem+json":{"schema":{"$ref":"#/components/schemas/Problem"}}}},"429":{"description":"Too many requests. Codes: rate_limited.","content":{"application/problem+json":{"schema":{"$ref":"#/components/schemas/Problem"}}}},"500":{"description":"Something went wrong. Codes: internal_error.","content":{"application/problem+json":{"schema":{"$ref":"#/components/schemas/Problem"}}}}}}},"/v1/organizations/{organizationId}/players/{playerId}/sessions":{"get":{"operationId":"listPlayerSessions","tags":["players"],"summary":"The player’s sessions here","description":"This organisation’s only, newest first. Empty until F-SES (M3) lands. Requires `players:read`.","x-clavian-permission":"players:read","parameters":[{"schema":{"$ref":"#/components/schemas/OrganizationId"},"required":true,"description":"A TypeID: \"org_\" followed by the base32 form of a UUIDv7.","name":"organizationId","in":"path"},{"schema":{"$ref":"#/components/schemas/PlayerId"},"required":true,"description":"A TypeID: \"ply_\" followed by the base32 form of a UUIDv7.","name":"playerId","in":"path"},{"schema":{"type":"string","minLength":1,"description":"The `page.cursor` from the previous response."},"required":false,"description":"The `page.cursor` from the previous response.","name":"cursor","in":"query"},{"schema":{"type":"integer","minimum":1,"maximum":100,"default":25,"description":"Items per page, 1–100."},"required":false,"description":"Items per page, 1–100.","name":"limit","in":"query"},{"schema":{"allOf":[{"$ref":"#/components/schemas/VenueId"},{"description":"Only this venue’s. A venue you do not work at is `404`, as it is in the path or in X-Clavian-Venue. Without it: every venue you work at."}]},"required":false,"description":"Only this venue’s. A venue you do not work at is `404`, as it is in the path or in X-Clavian-Venue. Without it: every venue you work at.","name":"venueId","in":"query"}],"responses":{"200":{"description":"A page of the tab.","content":{"application/json":{"schema":{"$ref":"#/components/schemas/PlayerSessionList"}}}},"400":{"description":"The request could not be understood. Codes: validation_failed.","content":{"application/problem+json":{"schema":{"$ref":"#/components/schemas/Problem"}}}},"401":{"description":"Not signed in. Codes: unauthenticated.","content":{"application/problem+json":{"schema":{"$ref":"#/components/schemas/Problem"}}}},"403":{"description":"Your role does not allow this. Codes: forbidden, second_factor_required.","content":{"application/problem+json":{"schema":{"$ref":"#/components/schemas/Problem"}}}},"404":{"description":"No such organisation, or no such player among its players — a player it has never dealt with included. Codes: not_found.","content":{"application/problem+json":{"schema":{"$ref":"#/components/schemas/Problem"}}}},"429":{"description":"Too many requests. Codes: rate_limited.","content":{"application/problem+json":{"schema":{"$ref":"#/components/schemas/Problem"}}}},"500":{"description":"Something went wrong. Codes: internal_error.","content":{"application/problem+json":{"schema":{"$ref":"#/components/schemas/Problem"}}}}}}},"/v1/organizations/{organizationId}/players/{playerId}/orders":{"get":{"operationId":"listPlayerOrders","tags":["players"],"summary":"The player’s orders here","description":"This organisation’s only, newest first. Empty until F-POS (M3) lands. Requires `players:read`.","x-clavian-permission":"players:read","parameters":[{"schema":{"$ref":"#/components/schemas/OrganizationId"},"required":true,"description":"A TypeID: \"org_\" followed by the base32 form of a UUIDv7.","name":"organizationId","in":"path"},{"schema":{"$ref":"#/components/schemas/PlayerId"},"required":true,"description":"A TypeID: \"ply_\" followed by the base32 form of a UUIDv7.","name":"playerId","in":"path"},{"schema":{"type":"string","minLength":1,"description":"The `page.cursor` from the previous response."},"required":false,"description":"The `page.cursor` from the previous response.","name":"cursor","in":"query"},{"schema":{"type":"integer","minimum":1,"maximum":100,"default":25,"description":"Items per page, 1–100."},"required":false,"description":"Items per page, 1–100.","name":"limit","in":"query"},{"schema":{"allOf":[{"$ref":"#/components/schemas/VenueId"},{"description":"Only this venue’s. A venue you do not work at is `404`, as it is in the path or in X-Clavian-Venue. Without it: every venue you work at."}]},"required":false,"description":"Only this venue’s. A venue you do not work at is `404`, as it is in the path or in X-Clavian-Venue. Without it: every venue you work at.","name":"venueId","in":"query"}],"responses":{"200":{"description":"A page of the tab.","content":{"application/json":{"schema":{"$ref":"#/components/schemas/PlayerOrderList"}}}},"400":{"description":"The request could not be understood. Codes: validation_failed.","content":{"application/problem+json":{"schema":{"$ref":"#/components/schemas/Problem"}}}},"401":{"description":"Not signed in. Codes: unauthenticated.","content":{"application/problem+json":{"schema":{"$ref":"#/components/schemas/Problem"}}}},"403":{"description":"Your role does not allow this. Codes: forbidden, second_factor_required.","content":{"application/problem+json":{"schema":{"$ref":"#/components/schemas/Problem"}}}},"404":{"description":"No such organisation, or no such player among its players — a player it has never dealt with included. Codes: not_found.","content":{"application/problem+json":{"schema":{"$ref":"#/components/schemas/Problem"}}}},"429":{"description":"Too many requests. Codes: rate_limited.","content":{"application/problem+json":{"schema":{"$ref":"#/components/schemas/Problem"}}}},"500":{"description":"Something went wrong. Codes: internal_error.","content":{"application/problem+json":{"schema":{"$ref":"#/components/schemas/Problem"}}}}}}},"/v1/organizations/{organizationId}/players/{playerId}/passes":{"get":{"operationId":"listPlayerPasses","tags":["players"],"summary":"The player’s passes here","description":"This organisation’s only, newest first. Empty until F-PRC (M3) lands. Requires `players:read`.","x-clavian-permission":"players:read","parameters":[{"schema":{"$ref":"#/components/schemas/OrganizationId"},"required":true,"description":"A TypeID: \"org_\" followed by the base32 form of a UUIDv7.","name":"organizationId","in":"path"},{"schema":{"$ref":"#/components/schemas/PlayerId"},"required":true,"description":"A TypeID: \"ply_\" followed by the base32 form of a UUIDv7.","name":"playerId","in":"path"},{"schema":{"type":"string","minLength":1,"description":"The `page.cursor` from the previous response."},"required":false,"description":"The `page.cursor` from the previous response.","name":"cursor","in":"query"},{"schema":{"type":"integer","minimum":1,"maximum":100,"default":25,"description":"Items per page, 1–100."},"required":false,"description":"Items per page, 1–100.","name":"limit","in":"query"}],"responses":{"200":{"description":"A page of the tab.","content":{"application/json":{"schema":{"$ref":"#/components/schemas/PlayerPassList"}}}},"400":{"description":"The request could not be understood. Codes: validation_failed.","content":{"application/problem+json":{"schema":{"$ref":"#/components/schemas/Problem"}}}},"401":{"description":"Not signed in. Codes: unauthenticated.","content":{"application/problem+json":{"schema":{"$ref":"#/components/schemas/Problem"}}}},"403":{"description":"Your role does not allow this. Codes: forbidden, second_factor_required.","content":{"application/problem+json":{"schema":{"$ref":"#/components/schemas/Problem"}}}},"404":{"description":"No such organisation, or no such player among its players — a player it has never dealt with included. Codes: not_found.","content":{"application/problem+json":{"schema":{"$ref":"#/components/schemas/Problem"}}}},"429":{"description":"Too many requests. Codes: rate_limited.","content":{"application/problem+json":{"schema":{"$ref":"#/components/schemas/Problem"}}}},"500":{"description":"Something went wrong. Codes: internal_error.","content":{"application/problem+json":{"schema":{"$ref":"#/components/schemas/Problem"}}}}}}},"/v1/organizations/{organizationId}/players/{playerId}/wallet":{"get":{"operationId":"listPlayerWallet","tags":["players"],"summary":"The player’s wallet statement here","description":"This organisation’s only, newest first. Empty until F-WAL (M3) lands. Requires `players:read`.","x-clavian-permission":"players:read","parameters":[{"schema":{"$ref":"#/components/schemas/OrganizationId"},"required":true,"description":"A TypeID: \"org_\" followed by the base32 form of a UUIDv7.","name":"organizationId","in":"path"},{"schema":{"$ref":"#/components/schemas/PlayerId"},"required":true,"description":"A TypeID: \"ply_\" followed by the base32 form of a UUIDv7.","name":"playerId","in":"path"},{"schema":{"type":"string","minLength":1,"description":"The `page.cursor` from the previous response."},"required":false,"description":"The `page.cursor` from the previous response.","name":"cursor","in":"query"},{"schema":{"type":"integer","minimum":1,"maximum":100,"default":25,"description":"Items per page, 1–100."},"required":false,"description":"Items per page, 1–100.","name":"limit","in":"query"}],"responses":{"200":{"description":"A page of the tab.","content":{"application/json":{"schema":{"$ref":"#/components/schemas/PlayerWalletList"}}}},"400":{"description":"The request could not be understood. Codes: validation_failed.","content":{"application/problem+json":{"schema":{"$ref":"#/components/schemas/Problem"}}}},"401":{"description":"Not signed in. Codes: unauthenticated.","content":{"application/problem+json":{"schema":{"$ref":"#/components/schemas/Problem"}}}},"403":{"description":"Your role does not allow this. Codes: forbidden, second_factor_required.","content":{"application/problem+json":{"schema":{"$ref":"#/components/schemas/Problem"}}}},"404":{"description":"No such organisation, or no such player among its players — a player it has never dealt with included. Codes: not_found.","content":{"application/problem+json":{"schema":{"$ref":"#/components/schemas/Problem"}}}},"429":{"description":"Too many requests. Codes: rate_limited.","content":{"application/problem+json":{"schema":{"$ref":"#/components/schemas/Problem"}}}},"500":{"description":"Something went wrong. Codes: internal_error.","content":{"application/problem+json":{"schema":{"$ref":"#/components/schemas/Problem"}}}}}}},"/v1/organizations/{organizationId}/players/{playerId}/points":{"get":{"operationId":"listPlayerPoints","tags":["players"],"summary":"The points the player earned here","description":"This organisation’s only, newest first. Empty until F-LOY (M3) lands. Requires `players:read`.","x-clavian-permission":"players:read","parameters":[{"schema":{"$ref":"#/components/schemas/OrganizationId"},"required":true,"description":"A TypeID: \"org_\" followed by the base32 form of a UUIDv7.","name":"organizationId","in":"path"},{"schema":{"$ref":"#/components/schemas/PlayerId"},"required":true,"description":"A TypeID: \"ply_\" followed by the base32 form of a UUIDv7.","name":"playerId","in":"path"},{"schema":{"type":"string","minLength":1,"description":"The `page.cursor` from the previous response."},"required":false,"description":"The `page.cursor` from the previous response.","name":"cursor","in":"query"},{"schema":{"type":"integer","minimum":1,"maximum":100,"default":25,"description":"Items per page, 1–100."},"required":false,"description":"Items per page, 1–100.","name":"limit","in":"query"}],"responses":{"200":{"description":"A page of the tab.","content":{"application/json":{"schema":{"$ref":"#/components/schemas/PlayerPointsList"}}}},"400":{"description":"The request could not be understood. Codes: validation_failed.","content":{"application/problem+json":{"schema":{"$ref":"#/components/schemas/Problem"}}}},"401":{"description":"Not signed in. Codes: unauthenticated.","content":{"application/problem+json":{"schema":{"$ref":"#/components/schemas/Problem"}}}},"403":{"description":"Your role does not allow this. Codes: forbidden, second_factor_required.","content":{"application/problem+json":{"schema":{"$ref":"#/components/schemas/Problem"}}}},"404":{"description":"No such organisation, or no such player among its players — a player it has never dealt with included. Codes: not_found.","content":{"application/problem+json":{"schema":{"$ref":"#/components/schemas/Problem"}}}},"429":{"description":"Too many requests. Codes: rate_limited.","content":{"application/problem+json":{"schema":{"$ref":"#/components/schemas/Problem"}}}},"500":{"description":"Something went wrong. Codes: internal_error.","content":{"application/problem+json":{"schema":{"$ref":"#/components/schemas/Problem"}}}}}}},"/v1/hq/organizations":{"get":{"operationId":"listHqOrganizations","tags":["hq"],"summary":"Every organisation on the Platform, newest first","description":"The organisations’ records: names, kind, PAN, registered address, HQ access level and status. No organisation’s customer data. Platform scope: requires `hq:organizations` and a second factor.","x-clavian-permission":"hq:organizations","parameters":[{"schema":{"type":"string","minLength":1,"description":"The `page.cursor` from the previous response."},"required":false,"description":"The `page.cursor` from the previous response.","name":"cursor","in":"query"},{"schema":{"type":"integer","minimum":1,"maximum":100,"default":25,"description":"Items per page, 1–100."},"required":false,"description":"Items per page, 1–100.","name":"limit","in":"query"}],"responses":{"200":{"description":"A page of organisations.","content":{"application/json":{"schema":{"$ref":"#/components/schemas/HqOrganizationList"}}}},"400":{"description":"The request could not be understood. Codes: validation_failed.","content":{"application/problem+json":{"schema":{"$ref":"#/components/schemas/Problem"}}}},"401":{"description":"Not signed in. Codes: unauthenticated.","content":{"application/problem+json":{"schema":{"$ref":"#/components/schemas/Problem"}}}},"403":{"description":"Not HQ, no `hq:organizations`, or no second factor on the session. Codes: forbidden, second_factor_required.","content":{"application/problem+json":{"schema":{"$ref":"#/components/schemas/Problem"}}}},"429":{"description":"Too many requests. Codes: rate_limited.","content":{"application/problem+json":{"schema":{"$ref":"#/components/schemas/Problem"}}}},"500":{"description":"Something went wrong. Codes: internal_error.","content":{"application/problem+json":{"schema":{"$ref":"#/components/schemas/Problem"}}}}}},"post":{"operationId":"createHqOrganization","tags":["hq"],"summary":"Create an organisation, and invite its owner","description":"Creates the organisation, `active`, in the deployment’s currency, and invites its owner by phone or email through the staff invitation (spec/07 §6.5): the link works once, for seven days, for someone signed in with that number or address, verified, with a second factor. `ownerInvitation.delivery` says whether the message went; a phone invitation is `not_sent` until SMS is connected, and `acceptUrl` is the link to pass on. HQ gains no membership. A slug already in use is `409 slug-taken`, so a retried create never makes a second organisation. Two audit rows in the new organisation’s log: its creation, and the invitation. Platform scope: requires `hq:organizations` and a second factor.","x-clavian-permission":"hq:organizations","requestBody":{"required":true,"content":{"application/json":{"schema":{"$ref":"#/components/schemas/CreateHqOrganization"}}}},"responses":{"201":{"description":"The organisation, and its owner’s invitation.","content":{"application/json":{"schema":{"$ref":"#/components/schemas/CreatedHqOrganization"}}}},"400":{"description":"The request could not be understood. Codes: validation_failed.","content":{"application/problem+json":{"schema":{"$ref":"#/components/schemas/Problem"}}}},"401":{"description":"Not signed in. Codes: unauthenticated.","content":{"application/problem+json":{"schema":{"$ref":"#/components/schemas/Problem"}}}},"403":{"description":"Not HQ, no `hq:organizations`, or no second factor on the session. Codes: forbidden, second_factor_required.","content":{"application/problem+json":{"schema":{"$ref":"#/components/schemas/Problem"}}}},"409":{"description":"Another organisation already has that slug. Codes: slug_taken.","content":{"application/problem+json":{"schema":{"$ref":"#/components/schemas/Problem"}}}},"429":{"description":"Too many requests. Codes: rate_limited.","content":{"application/problem+json":{"schema":{"$ref":"#/components/schemas/Problem"}}}},"500":{"description":"Something went wrong. Codes: internal_error.","content":{"application/problem+json":{"schema":{"$ref":"#/components/schemas/Problem"}}}}}}},"/v1/hq/organizations/{organizationId}":{"get":{"operationId":"getHqOrganization","tags":["hq"],"summary":"Read one organisation’s record","description":"Platform scope: requires `hq:organizations` and a second factor.","x-clavian-permission":"hq:organizations","parameters":[{"schema":{"$ref":"#/components/schemas/OrganizationId"},"required":true,"description":"A TypeID: \"org_\" followed by the base32 form of a UUIDv7.","name":"organizationId","in":"path"}],"responses":{"200":{"description":"The organisation.","content":{"application/json":{"schema":{"$ref":"#/components/schemas/HqOrganization"}}}},"400":{"description":"The request could not be understood. Codes: validation_failed.","content":{"application/problem+json":{"schema":{"$ref":"#/components/schemas/Problem"}}}},"401":{"description":"Not signed in. Codes: unauthenticated.","content":{"application/problem+json":{"schema":{"$ref":"#/components/schemas/Problem"}}}},"403":{"description":"Not HQ, no `hq:organizations`, or no second factor on the session. Codes: forbidden, second_factor_required.","content":{"application/problem+json":{"schema":{"$ref":"#/components/schemas/Problem"}}}},"404":{"description":"No such organisation. Codes: not_found.","content":{"application/problem+json":{"schema":{"$ref":"#/components/schemas/Problem"}}}},"429":{"description":"Too many requests. Codes: rate_limited.","content":{"application/problem+json":{"schema":{"$ref":"#/components/schemas/Problem"}}}},"500":{"description":"Something went wrong. Codes: internal_error.","content":{"application/problem+json":{"schema":{"$ref":"#/components/schemas/Problem"}}}}}}},"/v1/hq/organizations/{organizationId}/owner-invitations":{"post":{"operationId":"createHqOwnerInvitation","tags":["hq"],"summary":"Invite an owner to an organisation that has none","description":"For an organisation whose owner has not accepted yet — the invitation expired, or went to the wrong address. Refused with `409` once the organisation has an active owner: from then on its owners invite owners (spec/07 §6.5). Every owner invitation still pending is withdrawn, so only the newest link works. One audit row per invitation withdrawn and one for the new one, in the organisation’s log. Platform scope: requires `hq:organizations` and a second factor.","x-clavian-permission":"hq:organizations","parameters":[{"schema":{"$ref":"#/components/schemas/OrganizationId"},"required":true,"description":"A TypeID: \"org_\" followed by the base32 form of a UUIDv7.","name":"organizationId","in":"path"}],"requestBody":{"required":true,"content":{"application/json":{"schema":{"$ref":"#/components/schemas/OwnerInvitationContact"}}}},"responses":{"201":{"description":"The invitation, and whether its message went.","content":{"application/json":{"schema":{"$ref":"#/components/schemas/CreatedInvitation"}}}},"400":{"description":"The request could not be understood. Codes: validation_failed.","content":{"application/problem+json":{"schema":{"$ref":"#/components/schemas/Problem"}}}},"401":{"description":"Not signed in. Codes: unauthenticated.","content":{"application/problem+json":{"schema":{"$ref":"#/components/schemas/Problem"}}}},"403":{"description":"Not HQ, no `hq:organizations`, or no second factor on the session. Codes: forbidden, second_factor_required.","content":{"application/problem+json":{"schema":{"$ref":"#/components/schemas/Problem"}}}},"404":{"description":"No such organisation. Codes: not_found.","content":{"application/problem+json":{"schema":{"$ref":"#/components/schemas/Problem"}}}},"409":{"description":"The organisation already has an owner, or that person is already on its staff. Codes: conflict, already_a_member.","content":{"application/problem+json":{"schema":{"$ref":"#/components/schemas/Problem"}}}},"429":{"description":"Too many requests. Codes: rate_limited.","content":{"application/problem+json":{"schema":{"$ref":"#/components/schemas/Problem"}}}},"500":{"description":"Something went wrong. Codes: internal_error.","content":{"application/problem+json":{"schema":{"$ref":"#/components/schemas/Problem"}}}}}}},"/healthz":{"get":{"operationId":"getLiveness","tags":["meta"],"summary":"Liveness","description":"Is this process alive? Answered without touching any dependency.","responses":{"200":{"description":"The process is alive.","content":{"application/json":{"schema":{"$ref":"#/components/schemas/LivenessResponse"}}}}}}},"/readyz":{"get":{"operationId":"getReadiness","tags":["meta"],"summary":"Readiness","description":"Should this process be sent traffic? `503` with the same body when a dependency it cannot serve without is down.","responses":{"200":{"description":"Ready for traffic.","content":{"application/json":{"schema":{"$ref":"#/components/schemas/ReadinessResponse"}}}},"503":{"description":"Not ready; at least one dependency failed.","content":{"application/json":{"schema":{"$ref":"#/components/schemas/ReadinessResponse"}}}}}}},"/internal/v1/stations/events":{"post":{"operationId":"ingestStationEvents","tags":["internal"],"summary":"The realtime gateway forwards station events, hellos and command acks (micro-batched)","description":"Service-to-service only: `Authorization: Bearer` with a service JWT. Anything else is `404`, so from outside the private network the internal API is indistinguishable from nothing. Each station's events are processed at most once: those at or below its last processed `seq` are ignored, and the rest are recorded in the transaction that advances it (spec/09 §5.2).","requestBody":{"required":true,"content":{"application/json":{"schema":{"type":"object","properties":{"stations":{"type":"array","items":{"type":"object","properties":{"stationId":{"$ref":"#/components/schemas/StationId"},"organizationId":{"$ref":"#/components/schemas/OrganizationId"},"venueId":{"$ref":"#/components/schemas/VenueId"},"hello":{"type":"object","properties":{"id":{"type":"string","pattern":"^(?:msg|cmd)_[0-7][0123456789abcdefghjkmnpqrstvwxyz]{25}$"},"type":{"type":"string","minLength":1,"maxLength":128},"ts":{"$ref":"#/components/schemas/IsoDateTime"},"p":{"type":"object","properties":{},"additionalProperties":{}}},"required":["id","type","ts","p"]},"events":{"type":"array","items":{"type":"object","properties":{"id":{"type":"string","pattern":"^(?:msg|cmd)_[0-7][0123456789abcdefghjkmnpqrstvwxyz]{25}$"},"type":{"type":"string","minLength":1,"maxLength":128},"ts":{"$ref":"#/components/schemas/IsoDateTime"},"p":{"type":"object","properties":{},"additionalProperties":{}},"seq":{"type":"integer","minimum":1}},"required":["id","type","ts","p","seq"]},"maxItems":1000},"acks":{"type":"array","items":{"type":"object","properties":{"commandId":{"$ref":"#/components/schemas/StationCommandId"},"at":{"$ref":"#/components/schemas/IsoDateTime"},"p":{"type":"object","properties":{},"additionalProperties":{}}},"required":["commandId","at","p"]},"maxItems":1000},"delivered":{"type":"array","items":{"$ref":"#/components/schemas/StationCommandId"},"maxItems":1000}},"required":["stationId","organizationId","venueId","events","acks","delivered"]},"minItems":1,"maxItems":500}},"required":["stations"]}}}},"responses":{"200":{"description":"Each station's last processed seq, and its state after a hello.","content":{"application/json":{"schema":{"type":"object","properties":{"stations":{"type":"array","items":{"type":"object","properties":{"stationId":{"$ref":"#/components/schemas/StationId"},"upTo":{"type":"integer","minimum":0},"state":{"type":"object","properties":{},"additionalProperties":{}},"commands":{"type":"array","items":{"type":"object","properties":{"id":{"$ref":"#/components/schemas/StationCommandId"},"type":{"type":"string","minLength":1},"payload":{"type":"object","properties":{},"additionalProperties":{}},"createdAt":{"$ref":"#/components/schemas/IsoDateTime"}},"required":["id","type","payload","createdAt"]}}},"required":["stationId","upTo"]}}},"required":["stations"]}}}},"400":{"description":"The batch could not be understood. Codes: validation_failed.","content":{"application/problem+json":{"schema":{"$ref":"#/components/schemas/Problem"}}}},"404":{"description":"Not a service caller. Codes: not_found.","content":{"application/problem+json":{"schema":{"$ref":"#/components/schemas/Problem"}}}},"500":{"description":"Something went wrong; the gateway acknowledges nothing and the station replays. Codes: internal_error.","content":{"application/problem+json":{"schema":{"$ref":"#/components/schemas/Problem"}}}}}}},"/internal/v1/realtime/publish":{"post":{"operationId":"publishRealtimeFrame","tags":["internal"],"summary":"Deliver a frame on a channel, through whichever gateway node holds its sockets","description":"Service-to-service only: `Authorization: Bearer` with a service JWT. Anything else is `404`, so from outside the private network the internal API is indistinguishable from nothing. Served by the realtime gateway. Best effort, like all realtime delivery: what must survive is a row.","requestBody":{"required":true,"content":{"application/json":{"schema":{"type":"object","properties":{"channel":{"type":"string","pattern":"^station:[a-z][a-z_]*_[0-7][0123456789abcdefghjkmnpqrstvwxyz]{25}(?::(?:netstats|signal))?$|^member:[a-z][a-z_]*_[0-7][0123456789abcdefghjkmnpqrstvwxyz]{25}$|^player:[a-z][a-z_]*_[0-7][0123456789abcdefghjkmnpqrstvwxyz]{25}$|^venue:[a-z][a-z_]*_[0-7][0123456789abcdefghjkmnpqrstvwxyz]{25}:(?:floor|orders|kitchen|calls|reservations|public|display)$|^org:[a-z][a-z_]*_[0-7][0123456789abcdefghjkmnpqrstvwxyz]{25}:alerts$"},"frame":{"type":"object","properties":{},"additionalProperties":{}}},"required":["channel","frame"]}}}},"responses":{"200":{"description":"How many gateway nodes were listening on the channel.","content":{"application/json":{"schema":{"type":"object","properties":{"nodes":{"type":"integer","minimum":0}},"required":["nodes"]}}}},"400":{"description":"The frame could not be understood. Codes: validation_failed.","content":{"application/problem+json":{"schema":{"$ref":"#/components/schemas/Problem"}}}},"404":{"description":"Not a service caller. Codes: not_found.","content":{"application/problem+json":{"schema":{"$ref":"#/components/schemas/Problem"}}}}}}},"/internal/v1/realtime/access":{"post":{"operationId":"resolveRealtimeAccess","tags":["internal"],"summary":"A member's realtime access now: the venues they work at and the channel permissions they hold","description":"Service-to-service only: `Authorization: Bearer` with a service JWT. Anything else is `404`, so from outside the private network the internal API is indistinguishable from nothing. Read fresh from the database, by the function every permission check calls (spec/07 §6.3).","requestBody":{"required":true,"content":{"application/json":{"schema":{"type":"object","properties":{"organizationId":{"$ref":"#/components/schemas/OrganizationId"},"memberId":{"$ref":"#/components/schemas/MemberId"}},"required":["organizationId","memberId"]}}}},"responses":{"200":{"description":"The access, now.","content":{"application/json":{"schema":{"type":"object","properties":{"active":{"type":"boolean"},"venues":{"type":"array","items":{"$ref":"#/components/schemas/VenueId"}},"permissions":{"type":"array","items":{"$ref":"#/components/schemas/Permission"}},"perm":{"type":"string"}},"required":["active","venues","permissions","perm"]}}}},"400":{"description":"The request could not be understood. Codes: validation_failed.","content":{"application/problem+json":{"schema":{"$ref":"#/components/schemas/Problem"}}}},"404":{"description":"Not a service caller. Codes: not_found.","content":{"application/problem+json":{"schema":{"$ref":"#/components/schemas/Problem"}}}}}}},"/internal/v1/realtime/jwks":{"get":{"operationId":"getRealtimeJwks","tags":["internal"],"summary":"The public keys realtime tokens are signed with","description":"Service-to-service only: `Authorization: Bearer` with a service JWT. Anything else is `404`, so from outside the private network the internal API is indistinguishable from nothing. The keys of the api's JWKS, which signs every realtime token (spec/09 §1). The gateway refreshes them when a token names a key it has not seen, so a rotation reaches it without a restart.","responses":{"200":{"description":"The keys.","content":{"application/json":{"schema":{"type":"object","properties":{"keys":{"type":"array","items":{"type":"object","properties":{"kid":{"type":"string"},"kty":{"type":"string"},"crv":{"type":"string"},"x":{"type":"string"},"alg":{"type":"string"}},"required":["kty"]}}},"required":["keys"]}}}},"404":{"description":"Not a service caller. Codes: not_found.","content":{"application/problem+json":{"schema":{"$ref":"#/components/schemas/Problem"}}}}}}}},"webhooks":{}}