{"openapi":"3.1.0","info":{"title":"chat-api","description":"ChatApp application (chat-api) 1.0.96","version":"1.0.96"},"externalDocs":{},"servers":[{"url":"https://dev.banga.chat"}],"security":[{"BearerAuth":[]}],"tags":[{"name":"Authentication","description":"Phone-number OTP login, two-factor verification and access-token refresh."},{"name":"Users","description":"The caller's own profile, settings, avatar and two-factor setup, plus blocking other users."},{"name":"Devices","description":"Devices registered to the caller: push tokens, biometric keys and revocation."},{"name":"Contacts","description":"Connections between users and the requests that create them."},{"name":"Rooms","description":"Chat rooms: creating, joining, membership and administration, and the chat status snapshot."},{"name":"Messages","description":"Room messages, their comments and attachments, and speech-to-text for voice messages."},{"name":"Wallets","description":"Cryptocurrency wallets, balances, transactions, withdrawals, payment requests and exchange rates."},{"name":"Calls","description":"Audio and video call signalling: starting, answering, ending and ICE server credentials."},{"name":"End-to-end encryption","description":"End-to-end encryption key directory and relay."},{"name":"Utilities","description":"Small helpers that do not belong to a domain, such as geo-location of the caller's IP address."},{"name":"Apps","description":"The bot/app platform: the apps a user installed and their manifests, the public directory, installing and removing apps, and the keys that sign cards and session tokens."}],"paths":{"/api/messages/{roomId}":{"put":{"tags":["Messages"],"summary":"Post a message to a room","description":"Posts a message to the room as the caller; the `userId` in the body is ignored. When the body has no `geoLocation`, one is derived from the IP address of the request. The caller must be an enabled member of the room, and when the room does not let participants publish, or is a broadcast room, only its creator or admin may post. In an end-to-end encrypted room `message` must already be encrypted. The room the message goes to is the `roomId` in the body, which must match the path.","operationId":"createMessage","parameters":[{"name":"roomId","in":"path","description":"Id of the room","required":true,"schema":{"type":"string"}}],"requestBody":{"content":{"application/json":{"schema":{"$ref":"#/components/schemas/ChatMessage"}}},"required":true},"responses":{"200":{"description":"The id of the new message","content":{"*/*":{"schema":{"type":"string"}}}},"400":{"description":"Invalid request, or the referenced entity does not exist or is not accessible","content":{"application/json":{"schema":{"$ref":"#/components/schemas/ErrorResponse"}}}},"401":{"description":"The access token is missing, expired or invalid, or the caller is not allowed to do this","content":{"application/json":{"schema":{"$ref":"#/components/schemas/ErrorResponse"}}}},"500":{"description":"Unexpected server error","content":{"application/json":{"schema":{"$ref":"#/components/schemas/ErrorResponse"}}}}}}},"/api/messages/{messageId}/translations/{language}":{"put":{"tags":["Messages"],"summary":"Add, replace or remove one language of a broadcast post","description":"Sets the text of one language of a post in a broadcast room, as an administrator editing it after the fact. An empty body removes that language. The text is cleaned the way a posted message is. Only the room's creator or admin, or a site administrator, may do this, and only for a language the server supports that is not the one the post was written in. Nothing is pushed: devices that already received the post see the change the next time they load the room.","operationId":"editTranslation","parameters":[{"name":"messageId","in":"path","description":"Id of the message","required":true,"schema":{"type":"string"}},{"name":"language","in":"path","description":"Language code, such as `fr`","required":true,"schema":{"type":"string"}}],"requestBody":{"content":{"application/json":{"schema":{"type":"string"}}}},"responses":{"200":{"description":"Every language of the post after the change","content":{"*/*":{"schema":{"$ref":"#/components/schemas/PostTranslations"}}}},"400":{"description":"Invalid request, or the referenced entity does not exist or is not accessible","content":{"application/json":{"schema":{"$ref":"#/components/schemas/ErrorResponse"}}}},"401":{"description":"The access token is missing, expired or invalid, or the caller is not allowed to do this","content":{"application/json":{"schema":{"$ref":"#/components/schemas/ErrorResponse"}}}},"500":{"description":"Unexpected server error","content":{"application/json":{"schema":{"$ref":"#/components/schemas/ErrorResponse"}}}}}}},"/api/e2ee/identity":{"put":{"tags":["End-to-end encryption"],"summary":"Register the device's identity keys","description":"Records the two public keys of the device the access token was issued to, the key room keys are wrapped to and the key that signs the shares it sends, together with a proof that the device holds the signing key. Allowed once per device: a device that wants other keys must register as a new device. The devices of the user's rooms are told that a new device can receive room keys.","operationId":"registerIdentity","requestBody":{"content":{"application/json":{"schema":{"$ref":"#/components/schemas/E2eeIdentityRequest"}}},"required":true},"responses":{"204":{"description":"The keys are registered"},"409":{"description":"The device already has identity keys","content":{"application/json":{"schema":{"$ref":"#/components/schemas/ErrorResponse"}}}},"400":{"description":"Invalid request, or the referenced entity does not exist or is not accessible","content":{"application/json":{"schema":{"$ref":"#/components/schemas/ErrorResponse"}}}},"401":{"description":"The access token is missing, expired or invalid, or the caller is not allowed to do this","content":{"application/json":{"schema":{"$ref":"#/components/schemas/ErrorResponse"}}}},"500":{"description":"Unexpected server error","content":{"application/json":{"schema":{"$ref":"#/components/schemas/ErrorResponse"}}}}}}},"/api/e2ee/backup":{"get":{"tags":["End-to-end encryption"],"summary":"Get the caller's key backup","description":"Returns the caller's passphrase-protected backup of their room keys exactly as they uploaded it, or 204 when they have none. The server cannot read it and only ever returns it to its owner.","operationId":"getBackup","responses":{"200":{"description":"The stored backup","content":{"*/*":{"schema":{"$ref":"#/components/schemas/E2eeKeyBackupDto"}}}},"204":{"description":"The caller has no backup"},"400":{"description":"Invalid request, or the referenced entity does not exist or is not accessible","content":{"application/json":{"schema":{"$ref":"#/components/schemas/ErrorResponse"}}}},"401":{"description":"The access token is missing, expired or invalid, or the caller is not allowed to do this","content":{"application/json":{"schema":{"$ref":"#/components/schemas/ErrorResponse"}}}},"500":{"description":"Unexpected server error","content":{"application/json":{"schema":{"$ref":"#/components/schemas/ErrorResponse"}}}}}},"put":{"tags":["End-to-end encryption"],"summary":"Store the caller's key backup","description":"Stores the caller's backup, replacing any existing one. Its version must be higher than the stored one. The backup is encrypted on the caller's devices under a key derived from a passphrase the server never sees. Answers 400 when the version, key derivation name, its parameters or the ciphertext are missing, unknown or too long.","operationId":"putBackup","requestBody":{"content":{"application/json":{"schema":{"$ref":"#/components/schemas/E2eeKeyBackupRequest"}}},"required":true},"responses":{"204":{"description":"The backup is stored"},"409":{"description":"The stored backup has the same or a newer version","content":{"application/json":{"schema":{"$ref":"#/components/schemas/ErrorResponse"}}}},"400":{"description":"Invalid request, or the referenced entity does not exist or is not accessible","content":{"application/json":{"schema":{"$ref":"#/components/schemas/ErrorResponse"}}}},"401":{"description":"The access token is missing, expired or invalid, or the caller is not allowed to do this","content":{"application/json":{"schema":{"$ref":"#/components/schemas/ErrorResponse"}}}},"500":{"description":"Unexpected server error","content":{"application/json":{"schema":{"$ref":"#/components/schemas/ErrorResponse"}}}}}},"delete":{"tags":["End-to-end encryption"],"summary":"Delete the caller's key backup","description":"Deletes the caller's backup, if there is one.","operationId":"deleteBackup","responses":{"204":{"description":"The backup no longer exists"},"400":{"description":"Invalid request, or the referenced entity does not exist or is not accessible","content":{"application/json":{"schema":{"$ref":"#/components/schemas/ErrorResponse"}}}},"401":{"description":"The access token is missing, expired or invalid, or the caller is not allowed to do this","content":{"application/json":{"schema":{"$ref":"#/components/schemas/ErrorResponse"}}}},"500":{"description":"Unexpected server error","content":{"application/json":{"schema":{"$ref":"#/components/schemas/ErrorResponse"}}}}}}},"/api/devices/voip-push-token":{"put":{"tags":["Devices"],"summary":"Set the VoIP push token","description":"Registers or refreshes the PushKit VoIP push token, used to ring the device for incoming calls, of the device the access token was issued to.","operationId":"updateVoipPushToken","requestBody":{"content":{"application/json":{"schema":{"$ref":"#/components/schemas/VoipPushTokenUpdateRequest"}}},"required":true},"responses":{"204":{"description":"The token is stored"},"400":{"description":"Invalid request, or the referenced entity does not exist or is not accessible","content":{"application/json":{"schema":{"$ref":"#/components/schemas/ErrorResponse"}}}},"401":{"description":"The access token is missing, expired or invalid, or the caller is not allowed to do this","content":{"application/json":{"schema":{"$ref":"#/components/schemas/ErrorResponse"}}}},"500":{"description":"Unexpected server error","content":{"application/json":{"schema":{"$ref":"#/components/schemas/ErrorResponse"}}}}}}},"/api/devices/push-token":{"put":{"tags":["Devices"],"summary":"Set the push-notification token","description":"Registers or refreshes the FCM push token of the device the access token was issued to.","operationId":"updatePushToken","requestBody":{"content":{"application/json":{"schema":{"$ref":"#/components/schemas/PushTokenUpdateRequest"}}},"required":true},"responses":{"204":{"description":"The token is stored"},"400":{"description":"Invalid request, or the referenced entity does not exist or is not accessible","content":{"application/json":{"schema":{"$ref":"#/components/schemas/ErrorResponse"}}}},"401":{"description":"The access token is missing, expired or invalid, or the caller is not allowed to do this","content":{"application/json":{"schema":{"$ref":"#/components/schemas/ErrorResponse"}}}},"500":{"description":"Unexpected server error","content":{"application/json":{"schema":{"$ref":"#/components/schemas/ErrorResponse"}}}}}}},"/api/devices/biometric-key":{"put":{"tags":["Devices"],"summary":"Enroll a biometric signing key","description":"Enrolls, or replaces, the biometric-gated public key of the device the access token was issued to, so that wallet transactions from it require biometric confirmation. The request proves possession of the private key with a signature over a nonce. Replacing a key that is already enrolled also needs currentKeySignature (the current key's signature over BIOMETRIC_KEY_CHANGE_V1|REPLACE|<nonce>) or the code sent by POST /api/devices/biometric-key/code; without it the request is refused with 403.","operationId":"enrollBiometricKey","requestBody":{"content":{"application/json":{"schema":{"$ref":"#/components/schemas/BiometricKeyEnrollRequest"}}},"required":true},"responses":{"204":{"description":"The key is enrolled"},"403":{"description":"A key is enrolled and the proof is missing or invalid","content":{"application/json":{"schema":{"$ref":"#/components/schemas/ErrorResponse"}}}},"429":{"description":"Too many codes entered for the account's number","content":{"application/json":{"schema":{"$ref":"#/components/schemas/ErrorResponse"}}}},"400":{"description":"Invalid request, or the referenced entity does not exist or is not accessible","content":{"application/json":{"schema":{"$ref":"#/components/schemas/ErrorResponse"}}}},"401":{"description":"The access token is missing, expired or invalid, or the caller is not allowed to do this","content":{"application/json":{"schema":{"$ref":"#/components/schemas/ErrorResponse"}}}},"500":{"description":"Unexpected server error","content":{"application/json":{"schema":{"$ref":"#/components/schemas/ErrorResponse"}}}}}},"delete":{"tags":["Devices"],"summary":"Remove the biometric signing key","description":"Removes the biometric key of the device the access token was issued to, so wallet transactions from it no longer require biometric confirmation. Needs proof: the enrolled key's signature over BIOMETRIC_KEY_CHANGE_V1|REMOVE|<nonce>, or the code sent by POST /api/devices/biometric-key/code; without it the request is refused with 403. The account's devices get a notice. A device without a key gets 204.","operationId":"unenrollBiometricKey","requestBody":{"content":{"application/json":{"schema":{"$ref":"#/components/schemas/BiometricKeyRemoveRequest"}}}},"responses":{"204":{"description":"The key is removed"},"403":{"description":"The proof is missing or invalid","content":{"application/json":{"schema":{"$ref":"#/components/schemas/ErrorResponse"}}}},"429":{"description":"Too many codes entered for the account's number","content":{"application/json":{"schema":{"$ref":"#/components/schemas/ErrorResponse"}}}},"400":{"description":"Invalid request, or the referenced entity does not exist or is not accessible","content":{"application/json":{"schema":{"$ref":"#/components/schemas/ErrorResponse"}}}},"401":{"description":"The access token is missing, expired or invalid, or the caller is not allowed to do this","content":{"application/json":{"schema":{"$ref":"#/components/schemas/ErrorResponse"}}}},"500":{"description":"Unexpected server error","content":{"application/json":{"schema":{"$ref":"#/components/schemas/ErrorResponse"}}}}}}},"/api/wallets/{walletId}":{"get":{"tags":["Wallets"],"summary":"Get a wallet","description":"Returns one of the caller's wallets with its balance. A wallet that does not exist or belongs to someone else gives the same 400.","operationId":"getWalletDetails","parameters":[{"name":"walletId","in":"path","description":"Id of the wallet","required":true,"schema":{"type":"string"}}],"responses":{"200":{"description":"OK","content":{"*/*":{"schema":{"$ref":"#/components/schemas/WalletDto"}}}},"400":{"description":"Invalid request, or the referenced entity does not exist or is not accessible","content":{"application/json":{"schema":{"$ref":"#/components/schemas/ErrorResponse"}}}},"401":{"description":"The access token is missing, expired or invalid, or the caller is not allowed to do this","content":{"application/json":{"schema":{"$ref":"#/components/schemas/ErrorResponse"}}}},"500":{"description":"Unexpected server error","content":{"application/json":{"schema":{"$ref":"#/components/schemas/ErrorResponse"}}}}}},"post":{"tags":["Wallets"],"summary":"Rename a wallet","description":"Changes the display name of one of the caller's wallets. A wallet that does not exist or belongs to someone else gives the same 400.","operationId":"updateWalletName","parameters":[{"name":"walletId","in":"path","description":"Id of the wallet","required":true,"schema":{"type":"string"}},{"name":"name","in":"query","description":"The new name; surrounding whitespace is removed","required":true,"schema":{"type":"string"}}],"responses":{"200":{"description":"OK","content":{"*/*":{"schema":{"$ref":"#/components/schemas/WalletDto"}}}},"400":{"description":"Invalid request, or the referenced entity does not exist or is not accessible","content":{"application/json":{"schema":{"$ref":"#/components/schemas/ErrorResponse"}}}},"401":{"description":"The access token is missing, expired or invalid, or the caller is not allowed to do this","content":{"application/json":{"schema":{"$ref":"#/components/schemas/ErrorResponse"}}}},"500":{"description":"Unexpected server error","content":{"application/json":{"schema":{"$ref":"#/components/schemas/ErrorResponse"}}}}}}},"/api/wallet-withdrawals":{"post":{"tags":["Wallets"],"summary":"Withdraw to an external address","description":"Sends funds from one of the caller's wallets to an address on the blockchain. The checks (biometric confirmation, limits, ownership, balance) come first; the withdrawal is then debited and recorded before it is sent, so a lost answer from the chain never undoes the debit. A refused withdrawal is refunded. Repeating a request with the same requestId returns the first result. Its status moves to confirmed later.","operationId":"createWithdrawal","requestBody":{"content":{"application/json":{"schema":{"$ref":"#/components/schemas/WithdrawalCreateRequest"}}},"required":true},"responses":{"200":{"description":"OK","content":{"*/*":{"schema":{"$ref":"#/components/schemas/WithdrawalRequestDto"}}}},"400":{"description":"Invalid request, or the referenced entity does not exist or is not accessible","content":{"application/json":{"schema":{"$ref":"#/components/schemas/ErrorResponse"}}}},"401":{"description":"The access token is missing, expired or invalid, or the caller is not allowed to do this","content":{"application/json":{"schema":{"$ref":"#/components/schemas/ErrorResponse"}}}},"500":{"description":"Unexpected server error","content":{"application/json":{"schema":{"$ref":"#/components/schemas/ErrorResponse"}}}}}}},"/api/wallet-transactions":{"post":{"tags":["Wallets"],"summary":"Send funds between wallets","description":"Creates one transaction per request in the body. If the device the access token was issued to has a biometric key enrolled, every request must carry a valid biometric signature. Returns the created transactions.","operationId":"createWalletTransaction","requestBody":{"content":{"application/json":{"schema":{"type":"array","items":{"$ref":"#/components/schemas/WalletTransactionRequest"}}}},"required":true},"responses":{"200":{"description":"OK","content":{"*/*":{"schema":{"type":"array","items":{"$ref":"#/components/schemas/WalletTransactionDto"}}}}},"400":{"description":"Invalid request, or the referenced entity does not exist or is not accessible","content":{"application/json":{"schema":{"$ref":"#/components/schemas/ErrorResponse"}}}},"401":{"description":"The access token is missing, expired or invalid, or the caller is not allowed to do this","content":{"application/json":{"schema":{"$ref":"#/components/schemas/ErrorResponse"}}}},"500":{"description":"Unexpected server error","content":{"application/json":{"schema":{"$ref":"#/components/schemas/ErrorResponse"}}}}}}},"/api/wallet-swaps":{"get":{"tags":["Wallets"],"summary":"List the conversions of a wallet","description":"Returns the conversions into or out of one of the caller's wallets, newest first.","operationId":"getSwaps","parameters":[{"name":"walletId","in":"query","description":"Id of the wallet","required":true,"schema":{"type":"string"}},{"name":"pageSize","in":"query","description":"Conversions per page, at most 100","required":false,"schema":{"type":"integer","format":"int32","default":20}},{"name":"currentPage","in":"query","description":"Zero-based page","required":false,"schema":{"type":"integer","format":"int32","default":0}}],"responses":{"200":{"description":"OK","content":{"*/*":{"schema":{"$ref":"#/components/schemas/PagedModelWalletSwapDto"}}}},"400":{"description":"Invalid request, or the referenced entity does not exist or is not accessible","content":{"application/json":{"schema":{"$ref":"#/components/schemas/ErrorResponse"}}}},"401":{"description":"The access token is missing, expired or invalid, or the caller is not allowed to do this","content":{"application/json":{"schema":{"$ref":"#/components/schemas/ErrorResponse"}}}},"500":{"description":"Unexpected server error","content":{"application/json":{"schema":{"$ref":"#/components/schemas/ErrorResponse"}}}}}},"post":{"tags":["Wallets"],"summary":"Convert at a quoted price","description":"Accepts a quote: the wallet converted from is debited and the wallet converted into is credited, at the quoted price. A quote can be accepted once and only before it expires. The biometric check and the balance are verified first; if anything fails, neither wallet changes. The status is PENDING when the trade is filled later, in which case the credit follows.","operationId":"execute","requestBody":{"content":{"application/json":{"schema":{"$ref":"#/components/schemas/WalletSwapExecuteRequest"}}},"required":true},"responses":{"200":{"description":"OK","content":{"*/*":{"schema":{"$ref":"#/components/schemas/WalletSwapDto"}}}},"400":{"description":"Invalid request, or the referenced entity does not exist or is not accessible","content":{"application/json":{"schema":{"$ref":"#/components/schemas/ErrorResponse"}}}},"401":{"description":"The access token is missing, expired or invalid, or the caller is not allowed to do this","content":{"application/json":{"schema":{"$ref":"#/components/schemas/ErrorResponse"}}}},"500":{"description":"Unexpected server error","content":{"application/json":{"schema":{"$ref":"#/components/schemas/ErrorResponse"}}}}}}},"/api/wallet-swaps/quote":{"post":{"tags":["Wallets"],"summary":"Quote a conversion","description":"Returns the price of converting an amount from one of the caller's wallets into another, the fee, and the amount that would be received. Nothing is debited, so it can be called each time the user changes the amount. The quote can be accepted until `expiresAt`.","operationId":"quote","requestBody":{"content":{"application/json":{"schema":{"$ref":"#/components/schemas/WalletSwapQuoteRequest"}}},"required":true},"responses":{"200":{"description":"OK","content":{"*/*":{"schema":{"$ref":"#/components/schemas/WalletSwapQuoteDto"}}}},"400":{"description":"Invalid request, or the referenced entity does not exist or is not accessible","content":{"application/json":{"schema":{"$ref":"#/components/schemas/ErrorResponse"}}}},"401":{"description":"The access token is missing, expired or invalid, or the caller is not allowed to do this","content":{"application/json":{"schema":{"$ref":"#/components/schemas/ErrorResponse"}}}},"500":{"description":"Unexpected server error","content":{"application/json":{"schema":{"$ref":"#/components/schemas/ErrorResponse"}}}}}}},"/api/wallet-limits/requests":{"post":{"tags":["Wallets"],"summary":"Ask for higher sending limits","description":"Records a request for one of the tiers, or for no limit, for 7 days, 30 days or until changed, with an optional reason; a site administrator approves or refuses it and the caller's devices are told. Only from a device past its waiting period and with biometric confirmation on, signed with its key; one request may be open at a time, and after a refusal a new one only after 7 days.","operationId":"createRequest","requestBody":{"content":{"application/json":{"schema":{"$ref":"#/components/schemas/WalletLimitRequestCreateRequest"}}},"required":true},"responses":{"200":{"description":"OK","content":{"*/*":{"schema":{"$ref":"#/components/schemas/WalletLimitRequestDto"}}}},"400":{"description":"Invalid request, or the referenced entity does not exist or is not accessible","content":{"application/json":{"schema":{"$ref":"#/components/schemas/ErrorResponse"}}}},"401":{"description":"The access token is missing, expired or invalid, or the caller is not allowed to do this","content":{"application/json":{"schema":{"$ref":"#/components/schemas/ErrorResponse"}}}},"500":{"description":"Unexpected server error","content":{"application/json":{"schema":{"$ref":"#/components/schemas/ErrorResponse"}}}}}}},"/api/users/connections/{targetUserId}/disable-incoming-calls":{"get":{"tags":["Contacts"],"summary":"Check whether calls from a contact are blocked","description":"Returns `{\"disableIncomingCalls\": true|false}`, whether the caller has switched off incoming calls from a connected user. Answers 400 when the two are not connected.","operationId":"getDisableIncomingCalls","parameters":[{"name":"targetUserId","in":"path","description":"Id of the connected user","required":true,"schema":{"type":"string"}}],"responses":{"200":{"description":"OK","content":{"*/*":{"schema":{"type":"object","additionalProperties":{}}}}},"400":{"description":"Invalid request, or the referenced entity does not exist or is not accessible","content":{"application/json":{"schema":{"$ref":"#/components/schemas/ErrorResponse"}}}},"401":{"description":"The access token is missing, expired or invalid, or the caller is not allowed to do this","content":{"application/json":{"schema":{"$ref":"#/components/schemas/ErrorResponse"}}}},"500":{"description":"Unexpected server error","content":{"application/json":{"schema":{"$ref":"#/components/schemas/ErrorResponse"}}}}}},"post":{"tags":["Contacts"],"summary":"Block or allow calls from a contact","description":"Switches incoming calls from a connected user off or on and returns the new setting as `{\"disableIncomingCalls\": true|false}`. Answers 400 when the two are not connected.","operationId":"updateDisableIncomingCalls","parameters":[{"name":"targetUserId","in":"path","description":"Id of the connected user","required":true,"schema":{"type":"string"}},{"name":"disableIncomingCalls","in":"query","description":"true to stop calls from this user, false to allow them","required":true,"schema":{"type":"boolean"}}],"responses":{"200":{"description":"OK","content":{"*/*":{"schema":{"type":"object","additionalProperties":{}}}}},"400":{"description":"Invalid request, or the referenced entity does not exist or is not accessible","content":{"application/json":{"schema":{"$ref":"#/components/schemas/ErrorResponse"}}}},"401":{"description":"The access token is missing, expired or invalid, or the caller is not allowed to do this","content":{"application/json":{"schema":{"$ref":"#/components/schemas/ErrorResponse"}}}},"500":{"description":"Unexpected server error","content":{"application/json":{"schema":{"$ref":"#/components/schemas/ErrorResponse"}}}}}}},"/api/users/connection-requests":{"get":{"tags":["Contacts"],"summary":"List the connection requests the caller sent","description":"Returns one page of the connection requests the caller has sent, newest first, including those not answered yet.","operationId":"getUserConnectionRequestsByUserId","parameters":[{"name":"pageSize","in":"query","description":"Requests per page","required":false,"schema":{"type":"integer","format":"int32","default":100}},{"name":"currentPage","in":"query","description":"Zero-based page number","required":false,"schema":{"type":"integer","format":"int32","default":0}}],"responses":{"200":{"description":"OK","content":{"*/*":{"schema":{"$ref":"#/components/schemas/PagedModelUserConnectionRequestDto"}}}},"400":{"description":"Invalid request, or the referenced entity does not exist or is not accessible","content":{"application/json":{"schema":{"$ref":"#/components/schemas/ErrorResponse"}}}},"401":{"description":"The access token is missing, expired or invalid, or the caller is not allowed to do this","content":{"application/json":{"schema":{"$ref":"#/components/schemas/ErrorResponse"}}}},"500":{"description":"Unexpected server error","content":{"application/json":{"schema":{"$ref":"#/components/schemas/ErrorResponse"}}}}}},"post":{"tags":["Contacts"],"summary":"Send a connection request","description":"Asks someone to connect, identified by `email`, `phone` or the `recipientId` of an existing user; at least one is required. Someone who already uses the app is notified in the app, and by push notification when offline; anyone else is invited by email or SMS. Answers 400 for an invalid email or phone number, a request that already exists, an existing connection, a request to oneself, a block in either direction or a message over 128 characters, and 401 for an unknown `recipientId`.","operationId":"createUserConnectionRequest","parameters":[{"name":"email","in":"query","description":"Email address of the person to connect with","required":false,"schema":{"type":"string"}},{"name":"phone","in":"query","description":"Phone number of the person to connect with, in international format","required":false,"schema":{"type":"string"}},{"name":"recipientId","in":"query","description":"Id of an existing user to connect with","required":false,"schema":{"type":"string"}},{"name":"customMessage","in":"query","description":"Personal note for the recipient, up to 128 characters","required":false,"schema":{"type":"string","default":""}}],"responses":{"204":{"description":"The request was sent"},"400":{"description":"Invalid request, or the referenced entity does not exist or is not accessible","content":{"application/json":{"schema":{"$ref":"#/components/schemas/ErrorResponse"}}}},"401":{"description":"The access token is missing, expired or invalid, or the caller is not allowed to do this","content":{"application/json":{"schema":{"$ref":"#/components/schemas/ErrorResponse"}}}},"500":{"description":"Unexpected server error","content":{"application/json":{"schema":{"$ref":"#/components/schemas/ErrorResponse"}}}}}}},"/api/users/connection-requests/incoming/{userConnectionRequestId}":{"post":{"tags":["Contacts"],"summary":"Answer a connection request","description":"Accepts a request addressed to the caller, which connects the two users, or declines it. A declined request can carry a reason and can also block the sender. Answers 400 when the request does not exist or was not addressed to the caller.","operationId":"processUserConnectionRequestIncoming","parameters":[{"name":"userConnectionRequestId","in":"path","description":"Id of the connection request","required":true,"schema":{"type":"string"}},{"name":"accept","in":"query","description":"true to accept the request, false to decline it","required":true,"schema":{"type":"boolean"}},{"name":"reason","in":"query","description":"Why the request is declined; also recorded as the reason when the sender is blocked","required":false,"schema":{"type":"string","enum":["UNKNOWN","SPAM","HARASSMENT","INAPPROPRIATE_CONTENT","DONT_KNOW_PERSON","OTHER"]}},{"name":"block","in":"query","description":"When declining, also block the sender","required":false,"schema":{"type":"boolean","default":false}}],"responses":{"200":{"description":"The user who sent the request","content":{"*/*":{"schema":{"$ref":"#/components/schemas/UserDto"}}}},"400":{"description":"Invalid request, or the referenced entity does not exist or is not accessible","content":{"application/json":{"schema":{"$ref":"#/components/schemas/ErrorResponse"}}}},"401":{"description":"The access token is missing, expired or invalid, or the caller is not allowed to do this","content":{"application/json":{"schema":{"$ref":"#/components/schemas/ErrorResponse"}}}},"500":{"description":"Unexpected server error","content":{"application/json":{"schema":{"$ref":"#/components/schemas/ErrorResponse"}}}}}}},"/api/users/blocks":{"get":{"tags":["Users"],"summary":"List the users the caller has blocked","description":"Returns one page of the caller's blocks, most recently blocked first.","operationId":"getBlockedUsers","parameters":[{"name":"pageSize","in":"query","description":"Blocks per page","required":false,"schema":{"type":"integer","format":"int32","default":10000}},{"name":"currentPage","in":"query","description":"Zero-based page number","required":false,"schema":{"type":"integer","format":"int32","default":0}}],"responses":{"200":{"description":"OK","content":{"*/*":{"schema":{"$ref":"#/components/schemas/PagedModelUserBlockDto"}}}},"400":{"description":"Invalid request, or the referenced entity does not exist or is not accessible","content":{"application/json":{"schema":{"$ref":"#/components/schemas/ErrorResponse"}}}},"401":{"description":"The access token is missing, expired or invalid, or the caller is not allowed to do this","content":{"application/json":{"schema":{"$ref":"#/components/schemas/ErrorResponse"}}}},"500":{"description":"Unexpected server error","content":{"application/json":{"schema":{"$ref":"#/components/schemas/ErrorResponse"}}}}}},"post":{"tags":["Users"],"summary":"Block a user","description":"Blocks another user for the caller. Answers 400 when the caller tries to block themselves, the user does not exist, or the user is already blocked.","operationId":"blockUser","parameters":[{"name":"blockedUserId","in":"query","description":"Id of the user to block","required":true,"schema":{"type":"string"}},{"name":"reason","in":"query","description":"Why the user is blocked","required":false,"schema":{"type":"string","enum":["UNKNOWN","SPAM","HARASSMENT","INAPPROPRIATE_CONTENT","DONT_KNOW_PERSON","OTHER"]}}],"responses":{"200":{"description":"OK","content":{"*/*":{"schema":{"$ref":"#/components/schemas/UserBlockDto"}}}},"400":{"description":"Invalid request, or the referenced entity does not exist or is not accessible","content":{"application/json":{"schema":{"$ref":"#/components/schemas/ErrorResponse"}}}},"401":{"description":"The access token is missing, expired or invalid, or the caller is not allowed to do this","content":{"application/json":{"schema":{"$ref":"#/components/schemas/ErrorResponse"}}}},"500":{"description":"Unexpected server error","content":{"application/json":{"schema":{"$ref":"#/components/schemas/ErrorResponse"}}}}}}},"/api/users/2fa/enable":{"post":{"tags":["Users"],"summary":"Enable two-factor authentication","description":"Turns two-factor authentication on after checking a code from the authenticator app that was set up with the QR code or link. Does nothing if it is already on. Answers 400 for a wrong or non-numeric code.","operationId":"enable2Fa","parameters":[{"name":"code","in":"query","description":"Current six-digit code from the authenticator app","required":true,"schema":{"type":"string"}}],"responses":{"200":{"description":"The user, with two-factor authentication on","content":{"*/*":{"schema":{"$ref":"#/components/schemas/User"}}}},"400":{"description":"Invalid request, or the referenced entity does not exist or is not accessible","content":{"application/json":{"schema":{"$ref":"#/components/schemas/ErrorResponse"}}}},"401":{"description":"The access token is missing, expired or invalid, or the caller is not allowed to do this","content":{"application/json":{"schema":{"$ref":"#/components/schemas/ErrorResponse"}}}},"500":{"description":"Unexpected server error","content":{"application/json":{"schema":{"$ref":"#/components/schemas/ErrorResponse"}}}}}}},"/api/users/2fa/disable":{"post":{"tags":["Users"],"summary":"Disable two-factor authentication","description":"Turns two-factor authentication off after checking a code from the authenticator app, and replaces the stored secret so the old authenticator entry stops working. Does nothing if it is already off. Answers 400 for a wrong or non-numeric code.","operationId":"disable2Fa","parameters":[{"name":"code","in":"query","description":"Current six-digit code from the authenticator app","required":true,"schema":{"type":"string"}}],"responses":{"200":{"description":"The user, with two-factor authentication off","content":{"*/*":{"schema":{"$ref":"#/components/schemas/User"}}}},"400":{"description":"Invalid request, or the referenced entity does not exist or is not accessible","content":{"application/json":{"schema":{"$ref":"#/components/schemas/ErrorResponse"}}}},"401":{"description":"The access token is missing, expired or invalid, or the caller is not allowed to do this","content":{"application/json":{"schema":{"$ref":"#/components/schemas/ErrorResponse"}}}},"500":{"description":"Unexpected server error","content":{"application/json":{"schema":{"$ref":"#/components/schemas/ErrorResponse"}}}}}}},"/api/speech-to-text":{"post":{"tags":["Messages"],"summary":"Transcribe audio to text","description":"Converts a recorded voice message to text on the server, using Whisper. The transcript is returned in `text`.","operationId":"transcribeAudio","requestBody":{"content":{"multipart/form-data":{"schema":{"$ref":"#/components/schemas/AudioUploadForm"}}}},"responses":{"200":{"description":"The transcript, in `text`","content":{"*/*":{"schema":{"$ref":"#/components/schemas/SpeachToTextResponse"}}}},"400":{"description":"The file is empty or produced no transcript (`error` says which), or the request is otherwise invalid","content":{"application/json":{"schema":{"oneOf":[{"$ref":"#/components/schemas/SpeachToTextResponse"},{"$ref":"#/components/schemas/ErrorResponse"}]}}}},"401":{"description":"The access token is missing, expired or invalid, or the caller is not allowed to do this","content":{"application/json":{"schema":{"$ref":"#/components/schemas/ErrorResponse"}}}},"500":{"description":"Unexpected server error","content":{"application/json":{"schema":{"$ref":"#/components/schemas/ErrorResponse"}}}}}}},"/api/rooms/{roomId}":{"get":{"tags":["Rooms"],"summary":"Get a room","description":"Returns a room's basic details. Available for a room the caller created or administers, and for a public group the caller is an enabled member of; any other room gives 400.","operationId":"getRoomDetails","parameters":[{"name":"roomId","in":"path","description":"Id of the room","required":true,"schema":{"type":"string"}}],"responses":{"200":{"description":"OK","content":{"*/*":{"schema":{"$ref":"#/components/schemas/RoomDto"}}}},"400":{"description":"Invalid request, or the referenced entity does not exist or is not accessible","content":{"application/json":{"schema":{"$ref":"#/components/schemas/ErrorResponse"}}}},"401":{"description":"The access token is missing, expired or invalid, or the caller is not allowed to do this","content":{"application/json":{"schema":{"$ref":"#/components/schemas/ErrorResponse"}}}},"500":{"description":"Unexpected server error","content":{"application/json":{"schema":{"$ref":"#/components/schemas/ErrorResponse"}}}}}},"post":{"tags":["Rooms"],"summary":"Update a room","description":"Changes the name and description of a room, and for a public group also whether comments and participant posting are allowed. Only the room's creator or admin may do this, and not in a direct chat; anyone else gets 400. Renaming a public group also changes its readable link, keeping the old one as a redirect, and is refused when the new name maps to a link another room uses.","operationId":"updateRoomNameAndDescription","parameters":[{"name":"roomId","in":"path","description":"Id of the room","required":true,"schema":{"type":"string"}},{"name":"name","in":"query","description":"The new name, 3 to 50 characters","required":true,"schema":{"type":"string"}},{"name":"description","in":"query","description":"The new description, up to 250 characters","required":true,"schema":{"type":"string"}},{"name":"commentsEnabled","in":"query","description":"Whether members may comment on messages. Applied to public groups only, and only together with participantPublishEnabled","required":false,"schema":{"type":["boolean","null"]}},{"name":"participantPublishEnabled","in":"query","description":"Whether members other than the creator and admin may post. Applied to public groups only, and only together with commentsEnabled","required":false,"schema":{"type":["boolean","null"]}}],"responses":{"200":{"description":"OK","content":{"*/*":{"schema":{"$ref":"#/components/schemas/RoomDto"}}}},"400":{"description":"Invalid request, or the referenced entity does not exist or is not accessible","content":{"application/json":{"schema":{"$ref":"#/components/schemas/ErrorResponse"}}}},"401":{"description":"The access token is missing, expired or invalid, or the caller is not allowed to do this","content":{"application/json":{"schema":{"$ref":"#/components/schemas/ErrorResponse"}}}},"500":{"description":"Unexpected server error","content":{"application/json":{"schema":{"$ref":"#/components/schemas/ErrorResponse"}}}}}}},"/api/rooms/{roomId}/{participantId}/publish-enabled":{"post":{"tags":["Rooms"],"summary":"Allow or forbid a member to post","description":"Sets whether one member may post in the room. Only the room's creator or admin may do this; anyone else gets 400, as does a participant who is not an enabled member.","operationId":"updateRoomPublishEnabledForParticipant","parameters":[{"name":"roomId","in":"path","description":"Id of the room","required":true,"schema":{"type":"string"}},{"name":"participantId","in":"path","description":"Id of the member","required":true,"schema":{"type":"string"}},{"name":"publishEnabled","in":"query","description":"true to let the member post, false to stop them","required":true,"schema":{"type":"boolean"}}],"responses":{"204":{"description":"The setting is stored"},"400":{"description":"Invalid request, or the referenced entity does not exist or is not accessible","content":{"application/json":{"schema":{"$ref":"#/components/schemas/ErrorResponse"}}}},"401":{"description":"The access token is missing, expired or invalid, or the caller is not allowed to do this","content":{"application/json":{"schema":{"$ref":"#/components/schemas/ErrorResponse"}}}},"500":{"description":"Unexpected server error","content":{"application/json":{"schema":{"$ref":"#/components/schemas/ErrorResponse"}}}}}}},"/api/rooms/{roomId}/muted":{"get":{"tags":["Rooms"],"summary":"Check whether a room is muted","description":"Returns `{\"muted\": true|false}`, the caller's own notification setting for the room. The caller must be an enabled member.","operationId":"getRoomMuted","parameters":[{"name":"roomId","in":"path","description":"Id of the room","required":true,"schema":{"type":"string"}}],"responses":{"200":{"description":"OK","content":{"*/*":{"schema":{"type":"object","additionalProperties":{}}}}},"400":{"description":"Invalid request, or the referenced entity does not exist or is not accessible","content":{"application/json":{"schema":{"$ref":"#/components/schemas/ErrorResponse"}}}},"401":{"description":"The access token is missing, expired or invalid, or the caller is not allowed to do this","content":{"application/json":{"schema":{"$ref":"#/components/schemas/ErrorResponse"}}}},"500":{"description":"Unexpected server error","content":{"application/json":{"schema":{"$ref":"#/components/schemas/ErrorResponse"}}}}}},"post":{"tags":["Rooms"],"summary":"Mute or unmute a room","description":"Sets the caller's own notification setting for the room and returns it as `{\"muted\": true|false}`. The caller must be an enabled member.","operationId":"updateRoomMuted","parameters":[{"name":"roomId","in":"path","description":"Id of the room","required":true,"schema":{"type":"string"}},{"name":"muted","in":"query","description":"true to mute the room, false to unmute it","required":true,"schema":{"type":"boolean"}}],"responses":{"200":{"description":"OK","content":{"*/*":{"schema":{"type":"object","additionalProperties":{}}}}},"400":{"description":"Invalid request, or the referenced entity does not exist or is not accessible","content":{"application/json":{"schema":{"$ref":"#/components/schemas/ErrorResponse"}}}},"401":{"description":"The access token is missing, expired or invalid, or the caller is not allowed to do this","content":{"application/json":{"schema":{"$ref":"#/components/schemas/ErrorResponse"}}}},"500":{"description":"Unexpected server error","content":{"application/json":{"schema":{"$ref":"#/components/schemas/ErrorResponse"}}}}}}},"/api/rooms/{roomId}/join":{"post":{"tags":["Rooms"],"summary":"Join a public room","description":"Makes the caller a member of a public group, or back into one they left. New members start with the room muted. Answers 400 when the room is not a public group or the caller was removed by its admin. Joining a broadcast room that is visible to clients changes nothing, since everyone is already in it, and returns it in the caller's language.","operationId":"joinPublicRoom","parameters":[{"name":"roomId","in":"path","description":"Id of the room","required":true,"schema":{"type":"string"}}],"responses":{"200":{"description":"OK","content":{"*/*":{"schema":{"$ref":"#/components/schemas/RoomDto"}}}},"400":{"description":"Invalid request, or the referenced entity does not exist or is not accessible","content":{"application/json":{"schema":{"$ref":"#/components/schemas/ErrorResponse"}}}},"401":{"description":"The access token is missing, expired or invalid, or the caller is not allowed to do this","content":{"application/json":{"schema":{"$ref":"#/components/schemas/ErrorResponse"}}}},"500":{"description":"Unexpected server error","content":{"application/json":{"schema":{"$ref":"#/components/schemas/ErrorResponse"}}}}}}},"/api/rooms/search":{"post":{"tags":["Rooms"],"summary":"Search public rooms","description":"Returns one page of public groups, the largest first, either those matching a text query or simply the most popular ones.","operationId":"searchPublicRooms","requestBody":{"content":{"application/json":{"schema":{"$ref":"#/components/schemas/RoomSearchRequest"}}},"required":true},"responses":{"200":{"description":"OK","content":{"*/*":{"schema":{"$ref":"#/components/schemas/PagedModelRoomDto"}}}},"400":{"description":"Invalid request, or the referenced entity does not exist or is not accessible","content":{"application/json":{"schema":{"$ref":"#/components/schemas/ErrorResponse"}}}},"401":{"description":"The access token is missing, expired or invalid, or the caller is not allowed to do this","content":{"application/json":{"schema":{"$ref":"#/components/schemas/ErrorResponse"}}}},"500":{"description":"Unexpected server error","content":{"application/json":{"schema":{"$ref":"#/components/schemas/ErrorResponse"}}}}}}},"/api/payment-requests":{"post":{"tags":["Wallets"],"summary":"Create a payment request","description":"Asks one or more people to pay into one of the caller's wallets. The request holds one item for each recipient and amount in the body.","operationId":"createPaymentRequest","requestBody":{"content":{"application/json":{"schema":{"$ref":"#/components/schemas/PaymentRequestCreateRequest"}}},"required":true},"responses":{"200":{"description":"OK","content":{"*/*":{"schema":{"$ref":"#/components/schemas/PaymentRequestDto"}}}},"400":{"description":"Invalid request, or the referenced entity does not exist or is not accessible","content":{"application/json":{"schema":{"$ref":"#/components/schemas/ErrorResponse"}}}},"401":{"description":"The access token is missing, expired or invalid, or the caller is not allowed to do this","content":{"application/json":{"schema":{"$ref":"#/components/schemas/ErrorResponse"}}}},"500":{"description":"Unexpected server error","content":{"application/json":{"schema":{"$ref":"#/components/schemas/ErrorResponse"}}}}}}},"/api/payment-requests/{paymentRequestId}/cancel":{"post":{"tags":["Wallets"],"summary":"Cancel a payment request","description":"Cancels a payment request. Only the owner of the target wallet may do it, and only while none of its items has been paid.","operationId":"cancelPaymentRequest","parameters":[{"name":"paymentRequestId","in":"path","description":"Id of the payment request","required":true,"schema":{"type":"string"}}],"responses":{"200":{"description":"OK","content":{"*/*":{"schema":{"$ref":"#/components/schemas/PaymentRequestDto"}}}},"400":{"description":"Invalid request, or the referenced entity does not exist or is not accessible","content":{"application/json":{"schema":{"$ref":"#/components/schemas/ErrorResponse"}}}},"401":{"description":"The access token is missing, expired or invalid, or the caller is not allowed to do this","content":{"application/json":{"schema":{"$ref":"#/components/schemas/ErrorResponse"}}}},"500":{"description":"Unexpected server error","content":{"application/json":{"schema":{"$ref":"#/components/schemas/ErrorResponse"}}}}}}},"/api/messages/{roomId}/attachments":{"get":{"tags":["Messages"],"summary":"List the attachments of a room","description":"Returns one page of the attachments in a room, newest first. The caller must have access to the room.","operationId":"retrieveMessageAttachments","parameters":[{"name":"roomId","in":"path","description":"Id of the room","required":true,"schema":{"type":"string"}},{"name":"pageSize","in":"query","description":"Attachments per page","required":false,"schema":{"type":"integer","format":"int32","default":10}},{"name":"currentPage","in":"query","description":"Zero-based page number","required":false,"schema":{"type":"integer","format":"int32","default":0}}],"responses":{"200":{"description":"OK","content":{"*/*":{"schema":{"$ref":"#/components/schemas/PagedModelRoomMessageAttachmentDto"}}}},"400":{"description":"Invalid request, or the referenced entity does not exist or is not accessible","content":{"application/json":{"schema":{"$ref":"#/components/schemas/ErrorResponse"}}}},"401":{"description":"The access token is missing, expired or invalid, or the caller is not allowed to do this","content":{"application/json":{"schema":{"$ref":"#/components/schemas/ErrorResponse"}}}},"500":{"description":"Unexpected server error","content":{"application/json":{"schema":{"$ref":"#/components/schemas/ErrorResponse"}}}}}},"post":{"tags":["Messages"],"summary":"Upload attachments to a room","description":"Uploads one or more files to a room. When `payload` (a JSON `ChatMessage`) is given, that message is posted first and the files are attached to it. Files are processed in the background: images get a thumbnail, videos are converted to MP4, and the caller is told about progress and failures through notifications. The caller must be a member of the room who may publish, and in a broadcast room only its creator or admin may upload. Not accepted in end-to-end encrypted rooms, which use `/api/e2ee/rooms/{roomId}/attachments` instead (400). At most 10 files per request, and per file 25 MB for images, 50 MB for audio, 250 MB for video and 100 MB for anything else, by extension (400 naming the limit, before anything is posted). Answers 429 when the server's processing queue is full or the caller already has 20 files being processed.","operationId":"uploadMessageAttachments","parameters":[{"name":"roomId","in":"path","description":"Id of the room","required":true,"schema":{"type":"string"}}],"requestBody":{"content":{"multipart/form-data":{"schema":{"$ref":"#/components/schemas/AttachmentsUploadForm"}}}},"responses":{"204":{"description":"The upload was accepted and is being processed"},"429":{"description":"Too many uploads are being processed; try again in a minute","content":{"application/json":{"schema":{"$ref":"#/components/schemas/ErrorResponse"}}}},"400":{"description":"Invalid request, or the referenced entity does not exist or is not accessible","content":{"application/json":{"schema":{"$ref":"#/components/schemas/ErrorResponse"}}}},"401":{"description":"The access token is missing, expired or invalid, or the caller is not allowed to do this","content":{"application/json":{"schema":{"$ref":"#/components/schemas/ErrorResponse"}}}},"500":{"description":"Unexpected server error","content":{"application/json":{"schema":{"$ref":"#/components/schemas/ErrorResponse"}}}}}}},"/api/messages/{messageId}/tldr":{"post":{"tags":["Messages"],"summary":"Request a summary of a message","description":"Asks for a short TL;DR of a long message, in the caller's language. The summary is produced asynchronously and delivered to the caller's connected devices as a `chat.tldr-ready` event; this call only queues the request. The caller must be an enabled member of the message's room. Not available in end-to-end encrypted rooms, where the server cannot read the message.","operationId":"requestTldr","parameters":[{"name":"messageId","in":"path","description":"Id of the message to summarize","required":true,"schema":{"type":"string"}}],"responses":{"202":{"description":"The request was queued","content":{"*/*":{"schema":{"$ref":"#/components/schemas/TldrRequestedDto"}}}},"400":{"description":"Invalid request, or the referenced entity does not exist or is not accessible","content":{"application/json":{"schema":{"$ref":"#/components/schemas/ErrorResponse"}}}},"401":{"description":"The access token is missing, expired or invalid, or the caller is not allowed to do this","content":{"application/json":{"schema":{"$ref":"#/components/schemas/ErrorResponse"}}}},"500":{"description":"Unexpected server error","content":{"application/json":{"schema":{"$ref":"#/components/schemas/ErrorResponse"}}}}}}},"/api/messages/{messageId}/attachments/{attachmentId}/ticket":{"post":{"tags":["Messages"],"summary":"Get a short-lived link to an attachment","description":"Returns a link that opens this one attachment for a few minutes without the `Authorization` header. It is for requests the caller cannot put a header on: a browser playing a video or audio file, or saving a download. The caller must have access to the message's room. The link opens nothing else and cannot be used as an access token.","operationId":"createAttachmentTicket","parameters":[{"name":"messageId","in":"path","description":"Id of the message","required":true,"schema":{"type":"string"}},{"name":"attachmentId","in":"path","description":"Id of the attachment","required":true,"schema":{"type":"string"}}],"responses":{"200":{"description":"OK","content":{"*/*":{"schema":{"$ref":"#/components/schemas/MediaTicketDto"}}}},"400":{"description":"Invalid request, or the referenced entity does not exist or is not accessible","content":{"application/json":{"schema":{"$ref":"#/components/schemas/ErrorResponse"}}}},"401":{"description":"The access token is missing, expired or invalid, or the caller is not allowed to do this","content":{"application/json":{"schema":{"$ref":"#/components/schemas/ErrorResponse"}}}},"500":{"description":"Unexpected server error","content":{"application/json":{"schema":{"$ref":"#/components/schemas/ErrorResponse"}}}}}}},"/api/e2ee/rooms/{roomId}/keys":{"get":{"tags":["End-to-end encryption"],"summary":"Get the room key shares stored for this device","description":"Returns the room's current key version and the wrapped copies of the room key stored for the calling device, for every key version, with what is needed to check who produced each one. The caller must be an active member; answers 400 for a room that is not end-to-end encrypted.","operationId":"getKeyShares","parameters":[{"name":"roomId","in":"path","description":"Id of the end-to-end encrypted room","required":true,"schema":{"type":"string"}}],"responses":{"200":{"description":"OK","content":{"*/*":{"schema":{"$ref":"#/components/schemas/E2eeKeySharesDto"}}}},"400":{"description":"Invalid request, or the referenced entity does not exist or is not accessible","content":{"application/json":{"schema":{"$ref":"#/components/schemas/ErrorResponse"}}}},"401":{"description":"The access token is missing, expired or invalid, or the caller is not allowed to do this","content":{"application/json":{"schema":{"$ref":"#/components/schemas/ErrorResponse"}}}},"500":{"description":"Unexpected server error","content":{"application/json":{"schema":{"$ref":"#/components/schemas/ErrorResponse"}}}}}},"post":{"tags":["End-to-end encryption"],"summary":"Upload wrapped room keys","description":"Stores wrapped copies of one version of the room key. Without `create`, an existing version is filled in for devices that lack it. With `create`, the room's next version is made, as the first key or a rotation, and must carry a share for every eligible device of every active member so that nobody is locked out; the first upload for a version wins. The server never sees a room key. Answers 400 for an out-of-range version, a malformed share, a share for a device that may not receive it or a new version that misses a device.","operationId":"uploadKeyShares","parameters":[{"name":"roomId","in":"path","description":"Id of the end-to-end encrypted room","required":true,"schema":{"type":"string"}}],"requestBody":{"content":{"application/json":{"schema":{"$ref":"#/components/schemas/E2eeKeyShareUploadRequest"}}},"required":true},"responses":{"204":{"description":"The shares are stored"},"409":{"description":"Another device created that key version first","content":{"application/json":{"schema":{"$ref":"#/components/schemas/ErrorResponse"}}}},"429":{"description":"Too many calls; try again shortly","content":{"application/json":{"schema":{"$ref":"#/components/schemas/ErrorResponse"}}}},"400":{"description":"Invalid request, or the referenced entity does not exist or is not accessible","content":{"application/json":{"schema":{"$ref":"#/components/schemas/ErrorResponse"}}}},"401":{"description":"The access token is missing, expired or invalid, or the caller is not allowed to do this","content":{"application/json":{"schema":{"$ref":"#/components/schemas/ErrorResponse"}}}},"500":{"description":"Unexpected server error","content":{"application/json":{"schema":{"$ref":"#/components/schemas/ErrorResponse"}}}}}}},"/api/e2ee/rooms/{roomId}/keys/request":{"post":{"tags":["End-to-end encryption"],"summary":"Ask for a room key this device lacks","description":"Asks the other devices in the room to share a key version the calling device does not have. The request is pushed to every active member; a device that holds the key answers by uploading a share. Answers 400 when the calling device may not receive room keys.","operationId":"requestKeyShare","parameters":[{"name":"roomId","in":"path","description":"Id of the end-to-end encrypted room","required":true,"schema":{"type":"string"}}],"requestBody":{"content":{"application/json":{"schema":{"$ref":"#/components/schemas/E2eeKeyRequest"}}},"required":true},"responses":{"204":{"description":"The request was sent"},"429":{"description":"Too many calls; try again shortly","content":{"application/json":{"schema":{"$ref":"#/components/schemas/ErrorResponse"}}}},"400":{"description":"Invalid request, or the referenced entity does not exist or is not accessible","content":{"application/json":{"schema":{"$ref":"#/components/schemas/ErrorResponse"}}}},"401":{"description":"The access token is missing, expired or invalid, or the caller is not allowed to do this","content":{"application/json":{"schema":{"$ref":"#/components/schemas/ErrorResponse"}}}},"500":{"description":"Unexpected server error","content":{"application/json":{"schema":{"$ref":"#/components/schemas/ErrorResponse"}}}}}}},"/api/e2ee/rooms/{roomId}/attachments":{"post":{"tags":["End-to-end encryption"],"summary":"Upload an encrypted attachment","description":"Stores an attachment, and its thumbnail if there is one, that the app has already encrypted. The server never opens, converts or inspects them and never learns a name or a type; it only checks who may upload, the size and that the blob starts like an encrypted one. Uploads that no message uses are deleted after a while. Put the returned `attachmentId` in the `attachmentIds` of the message.","operationId":"uploadAttachment","parameters":[{"name":"roomId","in":"path","description":"Id of the end-to-end encrypted room","required":true,"schema":{"type":"string"}}],"requestBody":{"content":{"multipart/form-data":{"schema":{"$ref":"#/components/schemas/EncryptedAttachmentUploadForm"}}}},"responses":{"200":{"description":"The id of the stored attachment and the number of bytes stored","content":{"*/*":{"schema":{"$ref":"#/components/schemas/E2eeAttachmentUploadedDto"}}}},"429":{"description":"Too many calls; try again shortly","content":{"application/json":{"schema":{"$ref":"#/components/schemas/ErrorResponse"}}}},"400":{"description":"Invalid request, or the referenced entity does not exist or is not accessible","content":{"application/json":{"schema":{"$ref":"#/components/schemas/ErrorResponse"}}}},"401":{"description":"The access token is missing, expired or invalid, or the caller is not allowed to do this","content":{"application/json":{"schema":{"$ref":"#/components/schemas/ErrorResponse"}}}},"500":{"description":"Unexpected server error","content":{"application/json":{"schema":{"$ref":"#/components/schemas/ErrorResponse"}}}}}}},"/api/devices/{id}/revoke":{"post":{"tags":["Devices"],"summary":"Revoke a device","description":"Blocks one of the caller's devices from further access. The device the request came from cannot be revoked (400), and a device of another user gives 401.","operationId":"revokeDevice","parameters":[{"name":"id","in":"path","description":"Id of the device to revoke","required":true,"schema":{"type":"string"}}],"responses":{"204":{"description":"The device is revoked"},"400":{"description":"Invalid request, or the referenced entity does not exist or is not accessible","content":{"application/json":{"schema":{"$ref":"#/components/schemas/ErrorResponse"}}}},"401":{"description":"The access token is missing, expired or invalid, or the caller is not allowed to do this","content":{"application/json":{"schema":{"$ref":"#/components/schemas/ErrorResponse"}}}},"500":{"description":"Unexpected server error","content":{"application/json":{"schema":{"$ref":"#/components/schemas/ErrorResponse"}}}}}}},"/api/devices/biometric-key/code":{"post":{"tags":["Devices"],"summary":"Send a code to change the biometric key","description":"Sends a code by SMS to the account's phone number, to remove or replace the biometric key of the device the access token was issued to when the key cannot sign, for example after the phone's fingerprints changed. The code goes in the code field of the removal or enrollment request. Counts against the number's limit on codes sent (429). A device without a key, or a code sent earlier that has not expired, gives 400.","operationId":"requestBiometricKeyChangeCode","responses":{"200":{"description":"The code is sent","content":{"*/*":{"schema":{"$ref":"#/components/schemas/OtpChallengeCreated"}}}},"429":{"description":"Too many codes sent to the account's number","content":{"application/json":{"schema":{"$ref":"#/components/schemas/ErrorResponse"}}}},"400":{"description":"Invalid request, or the referenced entity does not exist or is not accessible","content":{"application/json":{"schema":{"$ref":"#/components/schemas/ErrorResponse"}}}},"401":{"description":"The access token is missing, expired or invalid, or the caller is not allowed to do this","content":{"application/json":{"schema":{"$ref":"#/components/schemas/ErrorResponse"}}}},"500":{"description":"Unexpected server error","content":{"application/json":{"schema":{"$ref":"#/components/schemas/ErrorResponse"}}}}}}},"/api/comments/{messageId}":{"get":{"tags":["Messages"],"summary":"List the comments on a message","description":"Returns one page of comments, the most reacted-to first and then the newest first. The caller must be an enabled member of the message's room, and the room must have comments enabled.","operationId":"fetchComments","parameters":[{"name":"messageId","in":"path","description":"Id of the message the comments belong to","required":true,"schema":{"type":"string"}},{"name":"pageNumber","in":"query","description":"Zero-based page number","required":true,"schema":{"type":"integer","format":"int32"}},{"name":"pageSize","in":"query","description":"Comments per page","required":false,"schema":{"type":"integer","format":"int32","default":20}}],"responses":{"200":{"description":"OK","content":{"*/*":{"schema":{"$ref":"#/components/schemas/PagedModelMessageCommentDto"}}}},"400":{"description":"Invalid request, or the referenced entity does not exist or is not accessible","content":{"application/json":{"schema":{"$ref":"#/components/schemas/ErrorResponse"}}}},"401":{"description":"The access token is missing, expired or invalid, or the caller is not allowed to do this","content":{"application/json":{"schema":{"$ref":"#/components/schemas/ErrorResponse"}}}},"500":{"description":"Unexpected server error","content":{"application/json":{"schema":{"$ref":"#/components/schemas/ErrorResponse"}}}}}},"post":{"tags":["Messages"],"summary":"Comment on a message","description":"Adds a comment by the caller to a message and delivers it to the members of the room. The caller must be an enabled member of the room, and the room must have comments enabled.","operationId":"createComment","parameters":[{"name":"messageId","in":"path","description":"Id of the message to comment on","required":true,"schema":{"type":"string"}},{"name":"comment","in":"query","description":"Text of the comment","required":true,"schema":{"type":"string"}}],"responses":{"200":{"description":"OK","content":{"*/*":{"schema":{"$ref":"#/components/schemas/MessageCommentDto"}}}},"400":{"description":"Invalid request, or the referenced entity does not exist or is not accessible","content":{"application/json":{"schema":{"$ref":"#/components/schemas/ErrorResponse"}}}},"401":{"description":"The access token is missing, expired or invalid, or the caller is not allowed to do this","content":{"application/json":{"schema":{"$ref":"#/components/schemas/ErrorResponse"}}}},"500":{"description":"Unexpected server error","content":{"application/json":{"schema":{"$ref":"#/components/schemas/ErrorResponse"}}}}}}},"/api/comments/{commentId}/remove-emoji":{"post":{"tags":["Messages"],"summary":"Remove a reaction from a comment","description":"Removes the caller's emoji reaction from a comment; nothing happens if the caller had not reacted with it. The caller must be an enabled member of the room, and the room must have comments enabled.","operationId":"removeEmojiForComment","parameters":[{"name":"commentId","in":"path","description":"Id of the comment","required":true,"schema":{"type":"string"}},{"name":"emoji","in":"query","description":"The emoji character","required":true,"schema":{"type":"string"}}],"responses":{"200":{"description":"The comment with its reactions after the change","content":{"*/*":{"schema":{"$ref":"#/components/schemas/MessageCommentDto"}}}},"400":{"description":"Invalid request, or the referenced entity does not exist or is not accessible","content":{"application/json":{"schema":{"$ref":"#/components/schemas/ErrorResponse"}}}},"401":{"description":"The access token is missing, expired or invalid, or the caller is not allowed to do this","content":{"application/json":{"schema":{"$ref":"#/components/schemas/ErrorResponse"}}}},"500":{"description":"Unexpected server error","content":{"application/json":{"schema":{"$ref":"#/components/schemas/ErrorResponse"}}}}}}},"/api/comments/{commentId}/add-emoji":{"post":{"tags":["Messages"],"summary":"React to a comment","description":"Adds the caller's emoji reaction to a comment; reacting again with the same emoji changes nothing. The caller must be an enabled member of the room, and the room must have comments enabled.","operationId":"addEmojiForComment","parameters":[{"name":"commentId","in":"path","description":"Id of the comment","required":true,"schema":{"type":"string"}},{"name":"emoji","in":"query","description":"The emoji character","required":true,"schema":{"type":"string"}}],"responses":{"200":{"description":"The comment with its reactions after the change","content":{"*/*":{"schema":{"$ref":"#/components/schemas/MessageCommentDto"}}}},"400":{"description":"Invalid request, or the referenced entity does not exist or is not accessible","content":{"application/json":{"schema":{"$ref":"#/components/schemas/ErrorResponse"}}}},"401":{"description":"The access token is missing, expired or invalid, or the caller is not allowed to do this","content":{"application/json":{"schema":{"$ref":"#/components/schemas/ErrorResponse"}}}},"500":{"description":"Unexpected server error","content":{"application/json":{"schema":{"$ref":"#/components/schemas/ErrorResponse"}}}}}}},"/api/calls":{"post":{"tags":["Calls"],"summary":"Start a call","description":"Starts a call to another user in a room and rings their devices. Answers 400 when the caller is not allowed to call that user in that room. The call then moves through the states returned in `CallResponse` as the callee accepts or declines and as either side ends it.","operationId":"createCall","requestBody":{"content":{"application/json":{"schema":{"$ref":"#/components/schemas/CallCreateRequest"}}},"required":true},"responses":{"200":{"description":"OK","content":{"*/*":{"schema":{"$ref":"#/components/schemas/CallResponse"}}}},"400":{"description":"Invalid request, or the referenced entity does not exist or is not accessible","content":{"application/json":{"schema":{"$ref":"#/components/schemas/ErrorResponse"}}}},"401":{"description":"The access token is missing, expired or invalid, or the caller is not allowed to do this","content":{"application/json":{"schema":{"$ref":"#/components/schemas/ErrorResponse"}}}},"500":{"description":"Unexpected server error","content":{"application/json":{"schema":{"$ref":"#/components/schemas/ErrorResponse"}}}}}}},"/api/calls/{callId}/reject":{"post":{"tags":["Calls"],"summary":"Decline an incoming call","description":"Declines an incoming call as its callee, and tells the caller. Exists for the same reason as accepting a call over REST.","operationId":"rejectCall","parameters":[{"name":"callId","in":"path","description":"Id of the call","required":true,"schema":{"type":"string"}}],"responses":{"200":{"description":"OK","content":{"*/*":{"schema":{"$ref":"#/components/schemas/CallResponse"}}}},"400":{"description":"Invalid request, or the referenced entity does not exist or is not accessible","content":{"application/json":{"schema":{"$ref":"#/components/schemas/ErrorResponse"}}}},"401":{"description":"The access token is missing, expired or invalid, or the caller is not allowed to do this","content":{"application/json":{"schema":{"$ref":"#/components/schemas/ErrorResponse"}}}},"500":{"description":"Unexpected server error","content":{"application/json":{"schema":{"$ref":"#/components/schemas/ErrorResponse"}}}}}}},"/api/calls/{callId}/end":{"post":{"tags":["Calls"],"summary":"End a call","description":"Hangs up a call in progress, and tells the other participant. Either participant may do it.","operationId":"endCall","parameters":[{"name":"callId","in":"path","description":"Id of the call","required":true,"schema":{"type":"string"}}],"responses":{"204":{"description":"The call is ended"},"400":{"description":"Invalid request, or the referenced entity does not exist or is not accessible","content":{"application/json":{"schema":{"$ref":"#/components/schemas/ErrorResponse"}}}},"401":{"description":"The access token is missing, expired or invalid, or the caller is not allowed to do this","content":{"application/json":{"schema":{"$ref":"#/components/schemas/ErrorResponse"}}}},"500":{"description":"Unexpected server error","content":{"application/json":{"schema":{"$ref":"#/components/schemas/ErrorResponse"}}}}}}},"/api/calls/{callId}/cancel":{"post":{"tags":["Calls"],"summary":"Cancel an outgoing call","description":"Withdraws a call the caller started that the callee has not answered yet, and tells the callee.","operationId":"cancelCall","parameters":[{"name":"callId","in":"path","description":"Id of the call","required":true,"schema":{"type":"string"}}],"responses":{"204":{"description":"The call is cancelled"},"400":{"description":"Invalid request, or the referenced entity does not exist or is not accessible","content":{"application/json":{"schema":{"$ref":"#/components/schemas/ErrorResponse"}}}},"401":{"description":"The access token is missing, expired or invalid, or the caller is not allowed to do this","content":{"application/json":{"schema":{"$ref":"#/components/schemas/ErrorResponse"}}}},"500":{"description":"Unexpected server error","content":{"application/json":{"schema":{"$ref":"#/components/schemas/ErrorResponse"}}}}}}},"/api/calls/{callId}/accept":{"post":{"tags":["Calls"],"summary":"Accept an incoming call","description":"Answers an incoming call as its callee, and tells the caller. Exists so that a native call screen opened by a push notification can answer before the app has a WebSocket connection; over WebSocket the same is done with a signalling message.","operationId":"acceptCall","parameters":[{"name":"callId","in":"path","description":"Id of the call","required":true,"schema":{"type":"string"}}],"responses":{"200":{"description":"OK","content":{"*/*":{"schema":{"$ref":"#/components/schemas/CallResponse"}}}},"400":{"description":"Invalid request, or the referenced entity does not exist or is not accessible","content":{"application/json":{"schema":{"$ref":"#/components/schemas/ErrorResponse"}}}},"401":{"description":"The access token is missing, expired or invalid, or the caller is not allowed to do this","content":{"application/json":{"schema":{"$ref":"#/components/schemas/ErrorResponse"}}}},"500":{"description":"Unexpected server error","content":{"application/json":{"schema":{"$ref":"#/components/schemas/ErrorResponse"}}}}}}},"/api/avatars/user":{"post":{"tags":["Users"],"summary":"Upload the caller's avatar","description":"Replaces the caller's avatar with the uploaded image, converted to a thumbnail.","operationId":"updateUserAvatarById","requestBody":{"content":{"multipart/form-data":{"schema":{"$ref":"#/components/schemas/ImageUploadForm"}}}},"responses":{"204":{"description":"The avatar is replaced"},"400":{"description":"Invalid request, or the referenced entity does not exist or is not accessible","content":{"application/json":{"schema":{"$ref":"#/components/schemas/ErrorResponse"}}}},"401":{"description":"The access token is missing, expired or invalid, or the caller is not allowed to do this","content":{"application/json":{"schema":{"$ref":"#/components/schemas/ErrorResponse"}}}},"500":{"description":"Unexpected server error","content":{"application/json":{"schema":{"$ref":"#/components/schemas/ErrorResponse"}}}}}}},"/api/avatars/user/reset":{"post":{"tags":["Users"],"summary":"Reset the caller's avatar","description":"Replaces the caller's avatar with a generated one based on their name.","operationId":"resetUserAvatarById","responses":{"204":{"description":"The avatar is reset"},"400":{"description":"Invalid request, or the referenced entity does not exist or is not accessible","content":{"application/json":{"schema":{"$ref":"#/components/schemas/ErrorResponse"}}}},"401":{"description":"The access token is missing, expired or invalid, or the caller is not allowed to do this","content":{"application/json":{"schema":{"$ref":"#/components/schemas/ErrorResponse"}}}},"500":{"description":"Unexpected server error","content":{"application/json":{"schema":{"$ref":"#/components/schemas/ErrorResponse"}}}}}}},"/api/avatars/room/{roomId}":{"get":{"tags":["Users"],"summary":"Get a room's avatar","description":"Returns the avatar of a room. Sent with `Cache-Control: no-cache`, so clients revalidate it.","operationId":"getAvatarByRoomId","parameters":[{"name":"roomId","in":"path","description":"Id of the room","required":true,"schema":{"type":"string"}}],"responses":{"200":{"description":"The image, as PNG","content":{"image/png":{"schema":{"type":"string","format":"binary"}}}},"400":{"description":"Invalid request, or the referenced entity does not exist or is not accessible","content":{"application/json":{"schema":{"$ref":"#/components/schemas/ErrorResponse"}}}},"401":{"description":"The access token is missing, expired or invalid, or the caller is not allowed to do this","content":{"application/json":{"schema":{"$ref":"#/components/schemas/ErrorResponse"}}}},"500":{"description":"Unexpected server error","content":{"application/json":{"schema":{"$ref":"#/components/schemas/ErrorResponse"}}}}}},"post":{"tags":["Users"],"summary":"Upload a room's avatar","description":"Replaces the avatar of a room with the uploaded image, converted to a thumbnail. Only the room's creator or admin may do this; anyone else gets 400. An image that cannot be converted also gives 400.","operationId":"updateRoomAvatarById","parameters":[{"name":"roomId","in":"path","description":"Id of the room","required":true,"schema":{"type":"string"}}],"requestBody":{"content":{"multipart/form-data":{"schema":{"$ref":"#/components/schemas/ImageUploadForm"}}}},"responses":{"204":{"description":"The avatar is replaced"},"400":{"description":"Invalid request, or the referenced entity does not exist or is not accessible","content":{"application/json":{"schema":{"$ref":"#/components/schemas/ErrorResponse"}}}},"401":{"description":"The access token is missing, expired or invalid, or the caller is not allowed to do this","content":{"application/json":{"schema":{"$ref":"#/components/schemas/ErrorResponse"}}}},"500":{"description":"Unexpected server error","content":{"application/json":{"schema":{"$ref":"#/components/schemas/ErrorResponse"}}}}}}},"/api/avatars/room/{roomId}/reset":{"post":{"tags":["Users"],"summary":"Reset a room's avatar","description":"Replaces the avatar of a room with a generated one based on its name. Only the room's creator or admin may do this; anyone else gets 400.","operationId":"resetRoomAvatarById","parameters":[{"name":"roomId","in":"path","description":"Id of the room","required":true,"schema":{"type":"string"}}],"responses":{"204":{"description":"The avatar is reset"},"400":{"description":"Invalid request, or the referenced entity does not exist or is not accessible","content":{"application/json":{"schema":{"$ref":"#/components/schemas/ErrorResponse"}}}},"401":{"description":"The access token is missing, expired or invalid, or the caller is not allowed to do this","content":{"application/json":{"schema":{"$ref":"#/components/schemas/ErrorResponse"}}}},"500":{"description":"Unexpected server error","content":{"application/json":{"schema":{"$ref":"#/components/schemas/ErrorResponse"}}}}}}},"/api/auth/token/refresh":{"post":{"tags":["Authentication"],"summary":"Refresh access token","description":"Exchanges a valid refresh token, with the id of the device it was issued to, for a new access token and refresh token. Needs no access token, since it is called once the old one has expired. A refresh token is good for one exchange. Presented again within a few seconds it is answered 409: another request has just exchanged it, and the caller should use the tokens that request received. Presented again later it is answered 400 and the session that descends from it is ended, so whoever holds the newer token has to sign in again. A session that has been kept alive by refreshes for longer than the maximum age is answered 400 too.","operationId":"refreshToken","requestBody":{"content":{"application/json":{"schema":{"$ref":"#/components/schemas/RefreshTokenRequest"}}},"required":true},"responses":{"200":{"description":"The new token pair","content":{"*/*":{"schema":{"$ref":"#/components/schemas/OAuth2TokenPair"}}}},"409":{"description":"Another request has just exchanged this refresh token","content":{"application/json":{"schema":{"$ref":"#/components/schemas/ErrorResponse"}}}},"400":{"description":"Invalid request, or the referenced entity does not exist or is not accessible","content":{"application/json":{"schema":{"$ref":"#/components/schemas/ErrorResponse"}}}},"500":{"description":"Unexpected server error","content":{"application/json":{"schema":{"$ref":"#/components/schemas/ErrorResponse"}}}}},"security":[]}},"/api/auth/logout":{"post":{"tags":["Authentication"],"summary":"Sign out","description":"Ends the refresh session of the given refresh token, so it can no longer be exchanged for new tokens. Always answers 204, whatever the token was, so the answer says nothing about it. The access token the device still holds expires by itself.","operationId":"logout","requestBody":{"content":{"application/json":{"schema":{"$ref":"#/components/schemas/LogoutRequest"}}},"required":true},"responses":{"204":{"description":"Signed out"},"400":{"description":"Invalid request, or the referenced entity does not exist or is not accessible","content":{"application/json":{"schema":{"$ref":"#/components/schemas/ErrorResponse"}}}},"500":{"description":"Unexpected server error","content":{"application/json":{"schema":{"$ref":"#/components/schemas/ErrorResponse"}}}}},"security":[]}},"/api/auth/generate-keys":{"post":{"tags":["Authentication"],"summary":"Generate an RSA key pair","description":"Generates a 2048-bit RSA key pair on the server and returns both keys in PEM format. The private key is not kept. Needs no access token.","operationId":"generateRsaKeys","responses":{"200":{"description":"OK","content":{"*/*":{"schema":{"$ref":"#/components/schemas/KeyPairPem"}}}},"400":{"description":"Invalid request, or the referenced entity does not exist or is not accessible","content":{"application/json":{"schema":{"$ref":"#/components/schemas/ErrorResponse"}}}},"500":{"description":"Unexpected server error","content":{"application/json":{"schema":{"$ref":"#/components/schemas/ErrorResponse"}}}}},"security":[]}},"/api/apps/{appId}/session-token":{"post":{"tags":["Apps"],"summary":"Issue a session token for a web app","description":"Returns a five-minute token, signed by the server, that the app's page hands to its own backend to prove which per-app user is there: a compact JWS with alg EdDSA, iss banga-apps, aud the app id, sub the per-app user id and scope the granted scopes, verifiable with the JWKS at /api/apps/signing-keys. The caller must have the app installed (or own or test it) and the app must be an active web app.","operationId":"sessionToken","parameters":[{"name":"appId","in":"path","description":"Id of the app","required":true,"schema":{"type":"string"}}],"responses":{"200":{"description":"The token","content":{"*/*":{"schema":{"$ref":"#/components/schemas/AppSessionTokenDto"}}}},"403":{"description":"The app is not installed or is not an active web app","content":{"application/json":{"schema":{"$ref":"#/components/schemas/ErrorResponse"}}}},"429":{"description":"Too many calls by this user or of this app","content":{"application/json":{"schema":{"$ref":"#/components/schemas/ErrorResponse"}}}},"400":{"description":"Invalid request, or the referenced entity does not exist or is not accessible","content":{"application/json":{"schema":{"$ref":"#/components/schemas/ErrorResponse"}}}},"401":{"description":"The access token is missing, expired or invalid, or the caller is not allowed to do this","content":{"application/json":{"schema":{"$ref":"#/components/schemas/ErrorResponse"}}}},"500":{"description":"Unexpected server error","content":{"application/json":{"schema":{"$ref":"#/components/schemas/ErrorResponse"}}}}}}},"/api/apps/{appId}/rooms/{roomId}":{"post":{"tags":["Apps"],"summary":"Add an app to a room as a room bot","description":"Lets the app post cards into the room on its own, with its bot token. The caller must be the room's admin or creator, the room must not be end-to-end encrypted, and the app must be active, published and declare roomBot in its manifest. The app reads nothing from the room.","operationId":"addToRoom","parameters":[{"name":"appId","in":"path","description":"Id of the app","required":true,"schema":{"type":"string"}},{"name":"roomId","in":"path","description":"Id of the room","required":true,"schema":{"type":"string"}}],"responses":{"204":{"description":"The app is in the room"},"403":{"description":"The app cannot be a room bot, or the room refuses apps","content":{"application/json":{"schema":{"$ref":"#/components/schemas/ErrorResponse"}}}},"400":{"description":"Invalid request, or the referenced entity does not exist or is not accessible","content":{"application/json":{"schema":{"$ref":"#/components/schemas/ErrorResponse"}}}},"401":{"description":"The access token is missing, expired or invalid, or the caller is not allowed to do this","content":{"application/json":{"schema":{"$ref":"#/components/schemas/ErrorResponse"}}}},"500":{"description":"Unexpected server error","content":{"application/json":{"schema":{"$ref":"#/components/schemas/ErrorResponse"}}}}}},"delete":{"tags":["Apps"],"summary":"Remove an app from a room","description":"Stops the app posting into the room. The caller must be the room's admin or creator.","operationId":"removeFromRoom","parameters":[{"name":"appId","in":"path","description":"Id of the app","required":true,"schema":{"type":"string"}},{"name":"roomId","in":"path","description":"Id of the room","required":true,"schema":{"type":"string"}}],"responses":{"204":{"description":"The app is no longer in the room"},"400":{"description":"Invalid request, or the referenced entity does not exist or is not accessible","content":{"application/json":{"schema":{"$ref":"#/components/schemas/ErrorResponse"}}}},"401":{"description":"The access token is missing, expired or invalid, or the caller is not allowed to do this","content":{"application/json":{"schema":{"$ref":"#/components/schemas/ErrorResponse"}}}},"500":{"description":"Unexpected server error","content":{"application/json":{"schema":{"$ref":"#/components/schemas/ErrorResponse"}}}}}}},"/api/apps/{appId}/report":{"post":{"tags":["Apps"],"summary":"Report an app","description":"Records a report for the site administrators, who can block the app for everyone.","operationId":"report","parameters":[{"name":"appId","in":"path","description":"Id of the app","required":true,"schema":{"type":"string"}}],"requestBody":{"content":{"application/json":{"schema":{"$ref":"#/components/schemas/AppReportRequest"}}},"required":true},"responses":{"204":{"description":"The report is recorded"},"400":{"description":"Invalid request, or the referenced entity does not exist or is not accessible","content":{"application/json":{"schema":{"$ref":"#/components/schemas/ErrorResponse"}}}},"401":{"description":"The access token is missing, expired or invalid, or the caller is not allowed to do this","content":{"application/json":{"schema":{"$ref":"#/components/schemas/ErrorResponse"}}}},"500":{"description":"Unexpected server error","content":{"application/json":{"schema":{"$ref":"#/components/schemas/ErrorResponse"}}}}}}},"/api/apps/{appId}/payments":{"post":{"tags":["Apps"],"summary":"Start a payment to an app","description":"Creates a pending payment the caller is asked to confirm on a native sheet, from a signed card's pay action (the amount, currency, reference and description are read from the signed card) or from a web app's request. Nothing moves until the payment is confirmed. The app must accept payments, the caller must have granted it the payments scope (or own or test it), and the amount must be within the app's limits. Asking again with a reference already paid is refused; a pending one is returned again.","operationId":"createPayment","parameters":[{"name":"appId","in":"path","description":"Id of the app","required":true,"schema":{"type":"string"}}],"requestBody":{"content":{"application/json":{"schema":{"$ref":"#/components/schemas/AppPaymentCreateRequest"}}},"required":true},"responses":{"200":{"description":"The pending payment, with the caller's balance before and after","content":{"*/*":{"schema":{"$ref":"#/components/schemas/AppPaymentDto"}}}},"400":{"description":"The request, amount, currency or reference is not valid, or a limit is reached","content":{"application/json":{"schema":{"$ref":"#/components/schemas/ErrorResponse"}}}},"403":{"description":"The app does not accept payments, the scope was not granted, or the app's payments are on hold","content":{"application/json":{"schema":{"$ref":"#/components/schemas/ErrorResponse"}}}},"429":{"description":"Too many calls by this user or of this app","content":{"application/json":{"schema":{"$ref":"#/components/schemas/ErrorResponse"}}}},"401":{"description":"The access token is missing, expired or invalid, or the caller is not allowed to do this","content":{"application/json":{"schema":{"$ref":"#/components/schemas/ErrorResponse"}}}},"500":{"description":"Unexpected server error","content":{"application/json":{"schema":{"$ref":"#/components/schemas/ErrorResponse"}}}}}}},"/api/apps/{appId}/payments/{paymentId}/decline":{"post":{"tags":["Apps"],"summary":"Decline a payment to an app","description":"The caller said no on the sheet; the app is told.","operationId":"declinePayment","parameters":[{"name":"appId","in":"path","description":"Id of the app","required":true,"schema":{"type":"string"}},{"name":"paymentId","in":"path","description":"Id of the payment","required":true,"schema":{"type":"string"}}],"responses":{"200":{"description":"The declined payment","content":{"*/*":{"schema":{"$ref":"#/components/schemas/AppPaymentDto"}}}},"400":{"description":"The payment is not the caller's or was already answered","content":{"application/json":{"schema":{"$ref":"#/components/schemas/ErrorResponse"}}}},"401":{"description":"The access token is missing, expired or invalid, or the caller is not allowed to do this","content":{"application/json":{"schema":{"$ref":"#/components/schemas/ErrorResponse"}}}},"500":{"description":"Unexpected server error","content":{"application/json":{"schema":{"$ref":"#/components/schemas/ErrorResponse"}}}}}}},"/api/apps/{appId}/payments/{paymentId}/confirm":{"post":{"tags":["Apps"],"summary":"Confirm a payment to an app","description":"Moves the amount from the caller's wallet to the app's wallet and tells the app. On a device that enrolled a biometric key the body must carry the signature over APP_PAYMENT_V1|paymentId|appId|amount|currency|nonce, the amount formatted with eight decimals and the currency as its unit (usdbg, btc, eth); the nonce is required always and used once. When the app's webhook answers the completed event in time, its reply (a private card such as a receipt, an ack or an error) is returned to show the caller.","operationId":"confirmPayment","parameters":[{"name":"appId","in":"path","description":"Id of the app","required":true,"schema":{"type":"string"}},{"name":"paymentId","in":"path","description":"Id of the payment","required":true,"schema":{"type":"string"}}],"requestBody":{"content":{"application/json":{"schema":{"$ref":"#/components/schemas/AppPaymentConfirmRequest"}}},"required":true},"responses":{"200":{"description":"The completed payment and, if any, the app's reply","content":{"*/*":{"schema":{"$ref":"#/components/schemas/AppPaymentConfirmReplyDto"}}}},"400":{"description":"The payment is not the caller's, is not pending or has expired, the balance is not enough, or a limit is reached","content":{"application/json":{"schema":{"$ref":"#/components/schemas/ErrorResponse"}}}},"401":{"description":"The device's biometric signature is missing or does not verify","content":{"application/json":{"schema":{"$ref":"#/components/schemas/ErrorResponse"}}}},"500":{"description":"Unexpected server error","content":{"application/json":{"schema":{"$ref":"#/components/schemas/ErrorResponse"}}}}}}},"/api/apps/{appId}/invocations":{"post":{"tags":["Apps"],"summary":"Run a command or card action of an app","description":"Sends what the device collected to the app's webhook, signed by the server, and returns the app's answer: a signed card (to post into the room or show privately), the next view of a dynamic flow, an acknowledgement, or the app's own error. The caller must have the app installed (or own or test it), be a member of the room, and the command must allow the room's type. Values are checked against the flow before anything is sent. The app learns a per-app id for the caller and a reference for the room, never the caller's or the room's real id.","operationId":"invoke","parameters":[{"name":"appId","in":"path","description":"Id of the app","required":true,"schema":{"type":"string"}}],"requestBody":{"content":{"application/json":{"schema":{"$ref":"#/components/schemas/AppInvocationRequest"}}},"required":true},"responses":{"200":{"description":"The app's answer","content":{"*/*":{"schema":{"$ref":"#/components/schemas/AppInvocationReplyDto"}}}},"403":{"description":"The app is not installed, not available, or the command is not allowed here","content":{"application/json":{"schema":{"$ref":"#/components/schemas/ErrorResponse"}}}},"422":{"description":"The app answered with its own error; the message is the app's","content":{"application/json":{"schema":{"$ref":"#/components/schemas/ErrorResponse"}}}},"429":{"description":"Too many invocations by this user or of this app","content":{"application/json":{"schema":{"$ref":"#/components/schemas/ErrorResponse"}}}},"502":{"description":"The app's reply broke the contract","content":{"application/json":{"schema":{"$ref":"#/components/schemas/ErrorResponse"}}}},"503":{"description":"The app cannot be called right now","content":{"application/json":{"schema":{"$ref":"#/components/schemas/ErrorResponse"}}}},"504":{"description":"The app did not answer in time","content":{"application/json":{"schema":{"$ref":"#/components/schemas/ErrorResponse"}}}},"400":{"description":"Invalid request, or the referenced entity does not exist or is not accessible","content":{"application/json":{"schema":{"$ref":"#/components/schemas/ErrorResponse"}}}},"401":{"description":"The access token is missing, expired or invalid, or the caller is not allowed to do this","content":{"application/json":{"schema":{"$ref":"#/components/schemas/ErrorResponse"}}}},"500":{"description":"Unexpected server error","content":{"application/json":{"schema":{"$ref":"#/components/schemas/ErrorResponse"}}}}}}},"/api/apps/{appId}/install":{"post":{"tags":["Apps"],"summary":"Install an app, granting it scopes","description":"Installs the app for the caller, or updates the scopes of an existing install. The scopes must be among those the manifest asks for. A reinstall after a plain remove keeps the per-app id the app knew the caller by; after a remove that asked to be forgotten it gets a new one.","operationId":"install","parameters":[{"name":"appId","in":"path","description":"Id of the app","required":true,"schema":{"type":"string"}}],"requestBody":{"content":{"application/json":{"schema":{"$ref":"#/components/schemas/AppInstallRequest"}}},"required":true},"responses":{"200":{"description":"The caller's apps after the install","content":{"*/*":{"schema":{"type":"array","items":{"$ref":"#/components/schemas/InstalledAppDto"}}}}},"403":{"description":"The app is private to others, blocked, suspended or has nothing to install","content":{"application/json":{"schema":{"$ref":"#/components/schemas/ErrorResponse"}}}},"400":{"description":"Invalid request, or the referenced entity does not exist or is not accessible","content":{"application/json":{"schema":{"$ref":"#/components/schemas/ErrorResponse"}}}},"401":{"description":"The access token is missing, expired or invalid, or the caller is not allowed to do this","content":{"application/json":{"schema":{"$ref":"#/components/schemas/ErrorResponse"}}}},"500":{"description":"Unexpected server error","content":{"application/json":{"schema":{"$ref":"#/components/schemas/ErrorResponse"}}}}}},"delete":{"tags":["Apps"],"summary":"Remove an app","description":"Removes the app from the caller's devices. With forget=true the per-app id the app knew the caller by is discarded, so the app's records no longer map to them and a later install looks like a new person. Removing an app that is not installed does nothing.","operationId":"remove","parameters":[{"name":"appId","in":"path","description":"Id of the app","required":true,"schema":{"type":"string"}},{"name":"forget","in":"query","description":"Whether to discard the per-app id as well","required":false,"schema":{"type":"boolean","default":false}}],"responses":{"204":{"description":"The app is removed"},"400":{"description":"Invalid request, or the referenced entity does not exist or is not accessible","content":{"application/json":{"schema":{"$ref":"#/components/schemas/ErrorResponse"}}}},"401":{"description":"The access token is missing, expired or invalid, or the caller is not allowed to do this","content":{"application/json":{"schema":{"$ref":"#/components/schemas/ErrorResponse"}}}},"500":{"description":"Unexpected server error","content":{"application/json":{"schema":{"$ref":"#/components/schemas/ErrorResponse"}}}}}}},"/api/apps/{appId}/cards/sign":{"post":{"tags":["Apps"],"summary":"Sign a card built by a web app","description":"Validates a card the app's page built against the contract and the app's manifest, and signs it for the caller so the device can post it into a room after the user confirms, exactly like a card an invocation returned. Requires the post.cards scope to be declared by the app and granted by the caller.","operationId":"signCard","parameters":[{"name":"appId","in":"path","description":"Id of the app","required":true,"schema":{"type":"string"}}],"requestBody":{"content":{"application/json":{"schema":{"$ref":"#/components/schemas/AppCardSignRequest"}}},"required":true},"responses":{"200":{"description":"The signed card","content":{"*/*":{"schema":{"$ref":"#/components/schemas/AppCardDto"}}}},"403":{"description":"The app is not installed, or post.cards was not granted","content":{"application/json":{"schema":{"$ref":"#/components/schemas/ErrorResponse"}}}},"400":{"description":"Invalid request, or the referenced entity does not exist or is not accessible","content":{"application/json":{"schema":{"$ref":"#/components/schemas/ErrorResponse"}}}},"401":{"description":"The access token is missing, expired or invalid, or the caller is not allowed to do this","content":{"application/json":{"schema":{"$ref":"#/components/schemas/ErrorResponse"}}}},"500":{"description":"Unexpected server error","content":{"application/json":{"schema":{"$ref":"#/components/schemas/ErrorResponse"}}}}}}},"/api/users/settings":{"get":{"tags":["Users"],"summary":"Get the caller's settings","description":"Returns the caller's preferences, such as language, currency, theme and notification status.","operationId":"getUserSettings","responses":{"200":{"description":"OK","content":{"*/*":{"schema":{"$ref":"#/components/schemas/UserSettingsDto"}}}},"400":{"description":"Invalid request, or the referenced entity does not exist or is not accessible","content":{"application/json":{"schema":{"$ref":"#/components/schemas/ErrorResponse"}}}},"401":{"description":"The access token is missing, expired or invalid, or the caller is not allowed to do this","content":{"application/json":{"schema":{"$ref":"#/components/schemas/ErrorResponse"}}}},"500":{"description":"Unexpected server error","content":{"application/json":{"schema":{"$ref":"#/components/schemas/ErrorResponse"}}}}}},"patch":{"tags":["Users"],"summary":"Update the caller's settings","description":"Changes only the fields present in the body; absent fields are left alone. A `currency` the exchange-rate table does not know is ignored. Answers 400 when `language` is not supported, `email` is malformed or used by another user, or `requestBiometricOnStart` is true on a device with no biometric key enrolled. Setting `name` completes the registration of a new user. Every entry in `contacts` with a valid email or phone number sends that person a connection request.","operationId":"updateUserSettings","requestBody":{"content":{"application/json":{"schema":{"$ref":"#/components/schemas/UserSettingsUpdateRequest"}}},"required":true},"responses":{"200":{"description":"OK","content":{"*/*":{"schema":{"$ref":"#/components/schemas/UserSettingsDto"}}}},"400":{"description":"Invalid request, or the referenced entity does not exist or is not accessible","content":{"application/json":{"schema":{"$ref":"#/components/schemas/ErrorResponse"}}}},"401":{"description":"The access token is missing, expired or invalid, or the caller is not allowed to do this","content":{"application/json":{"schema":{"$ref":"#/components/schemas/ErrorResponse"}}}},"500":{"description":"Unexpected server error","content":{"application/json":{"schema":{"$ref":"#/components/schemas/ErrorResponse"}}}}}}},"/api/wallets/{walletId}/deposit-address":{"get":{"tags":["Wallets"],"summary":"Get a wallet's deposit address","description":"Returns the address other people can send funds to for this wallet. The address is derived the first time it is asked for and is the same afterwards.","operationId":"getDepositAddress","parameters":[{"name":"walletId","in":"path","description":"Id of the wallet","required":true,"schema":{"type":"string"}}],"responses":{"200":{"description":"OK","content":{"*/*":{"schema":{"$ref":"#/components/schemas/WalletDepositAddressDto"}}}},"400":{"description":"Invalid request, or the referenced entity does not exist or is not accessible","content":{"application/json":{"schema":{"$ref":"#/components/schemas/ErrorResponse"}}}},"401":{"description":"The access token is missing, expired or invalid, or the caller is not allowed to do this","content":{"application/json":{"schema":{"$ref":"#/components/schemas/ErrorResponse"}}}},"500":{"description":"Unexpected server error","content":{"application/json":{"schema":{"$ref":"#/components/schemas/ErrorResponse"}}}}}}},"/api/wallets/list":{"get":{"tags":["Wallets"],"summary":"List the caller's wallets","description":"Returns every wallet the caller owns, with its balance, ordered by name.","operationId":"getUserWallets","responses":{"200":{"description":"OK","content":{"*/*":{"schema":{"type":"array","items":{"$ref":"#/components/schemas/WalletDto"}}}}},"400":{"description":"Invalid request, or the referenced entity does not exist or is not accessible","content":{"application/json":{"schema":{"$ref":"#/components/schemas/ErrorResponse"}}}},"401":{"description":"The access token is missing, expired or invalid, or the caller is not allowed to do this","content":{"application/json":{"schema":{"$ref":"#/components/schemas/ErrorResponse"}}}},"500":{"description":"Unexpected server error","content":{"application/json":{"schema":{"$ref":"#/components/schemas/ErrorResponse"}}}}}}},"/api/wallet-withdrawals/{withdrawalRequestId}":{"get":{"tags":["Wallets"],"summary":"Get a withdrawal","description":"Returns the current state of one of the caller's withdrawals.","operationId":"getWithdrawal","parameters":[{"name":"withdrawalRequestId","in":"path","description":"Id of the withdrawal","required":true,"schema":{"type":"string"}}],"responses":{"200":{"description":"OK","content":{"*/*":{"schema":{"$ref":"#/components/schemas/WithdrawalRequestDto"}}}},"400":{"description":"Invalid request, or the referenced entity does not exist or is not accessible","content":{"application/json":{"schema":{"$ref":"#/components/schemas/ErrorResponse"}}}},"401":{"description":"The access token is missing, expired or invalid, or the caller is not allowed to do this","content":{"application/json":{"schema":{"$ref":"#/components/schemas/ErrorResponse"}}}},"500":{"description":"Unexpected server error","content":{"application/json":{"schema":{"$ref":"#/components/schemas/ErrorResponse"}}}}}}},"/api/wallet-withdrawals/quote":{"get":{"tags":["Wallets"],"summary":"Quote a withdrawal fee","description":"Computes the network fee a withdrawal would cost and whether the wallet's balance covers the amount plus the fee. Nothing is signed, broadcast or debited, so it can be called repeatedly while the user edits the amount. The fee is worked out again when the withdrawal is actually made, because network conditions can change in between.","operationId":"quoteWithdrawal","parameters":[{"name":"walletId","in":"query","description":"Id of the wallet to withdraw from","required":true,"schema":{"type":"string"}},{"name":"toAddress","in":"query","description":"Destination address, valid for the wallet's network","required":true,"schema":{"type":"string"}},{"name":"amount","in":"query","description":"Amount, in the wallet's native unit","required":true,"schema":{"type":"number"}}],"responses":{"200":{"description":"OK","content":{"*/*":{"schema":{"$ref":"#/components/schemas/WithdrawalQuoteDto"}}}},"400":{"description":"Invalid request, or the referenced entity does not exist or is not accessible","content":{"application/json":{"schema":{"$ref":"#/components/schemas/ErrorResponse"}}}},"401":{"description":"The access token is missing, expired or invalid, or the caller is not allowed to do this","content":{"application/json":{"schema":{"$ref":"#/components/schemas/ErrorResponse"}}}},"500":{"description":"Unexpected server error","content":{"application/json":{"schema":{"$ref":"#/components/schemas/ErrorResponse"}}}}}}},"/api/wallet-transactions/{walletId}":{"get":{"tags":["Wallets"],"summary":"List a wallet's transactions","description":"Returns one page of the transactions of one of the caller's wallets.","operationId":"getWalletTransactions","parameters":[{"name":"walletId","in":"path","description":"Id of the wallet","required":true,"schema":{"type":"string"}},{"name":"pageSize","in":"query","description":"Transactions per page","required":false,"schema":{"type":"integer","format":"int32","default":10}},{"name":"currentPage","in":"query","description":"Zero-based page number","required":false,"schema":{"type":"integer","format":"int32","default":0}}],"responses":{"200":{"description":"OK","content":{"*/*":{"schema":{"$ref":"#/components/schemas/PagedModelWalletTransactionDto"}}}},"400":{"description":"Invalid request, or the referenced entity does not exist or is not accessible","content":{"application/json":{"schema":{"$ref":"#/components/schemas/ErrorResponse"}}}},"401":{"description":"The access token is missing, expired or invalid, or the caller is not allowed to do this","content":{"application/json":{"schema":{"$ref":"#/components/schemas/ErrorResponse"}}}},"500":{"description":"Unexpected server error","content":{"application/json":{"schema":{"$ref":"#/components/schemas/ErrorResponse"}}}}}}},"/api/wallet-transactions/transaction/{transactionId}":{"get":{"tags":["Wallets"],"summary":"Get a transaction","description":"Returns one transaction. Only its sender and recipient may read it.","operationId":"getWalletTransaction","parameters":[{"name":"transactionId","in":"path","description":"Id of the transaction","required":true,"schema":{"type":"string"}}],"responses":{"200":{"description":"OK","content":{"*/*":{"schema":{"$ref":"#/components/schemas/WalletTransactionDto"}}}},"400":{"description":"Invalid request, or the referenced entity does not exist or is not accessible","content":{"application/json":{"schema":{"$ref":"#/components/schemas/ErrorResponse"}}}},"401":{"description":"The access token is missing, expired or invalid, or the caller is not allowed to do this","content":{"application/json":{"schema":{"$ref":"#/components/schemas/ErrorResponse"}}}},"500":{"description":"Unexpected server error","content":{"application/json":{"schema":{"$ref":"#/components/schemas/ErrorResponse"}}}}}}},"/api/wallet-swaps/{swapId}":{"get":{"tags":["Wallets"],"summary":"Get a conversion","description":"Returns the current state of one of the caller's conversions.","operationId":"getSwap","parameters":[{"name":"swapId","in":"path","description":"Id of the conversion","required":true,"schema":{"type":"string"}}],"responses":{"200":{"description":"OK","content":{"*/*":{"schema":{"$ref":"#/components/schemas/WalletSwapDto"}}}},"400":{"description":"Invalid request, or the referenced entity does not exist or is not accessible","content":{"application/json":{"schema":{"$ref":"#/components/schemas/ErrorResponse"}}}},"401":{"description":"The access token is missing, expired or invalid, or the caller is not allowed to do this","content":{"application/json":{"schema":{"$ref":"#/components/schemas/ErrorResponse"}}}},"500":{"description":"Unexpected server error","content":{"application/json":{"schema":{"$ref":"#/components/schemas/ErrorResponse"}}}}}}},"/api/wallet-swaps/pairs":{"get":{"tags":["Wallets"],"summary":"List the conversions available","description":"Returns every direction a conversion can be made in, with the smallest and largest amount of each as a USD value. A direction that is switched off is returned with `enabled` false.","operationId":"getPairs","responses":{"200":{"description":"OK","content":{"*/*":{"schema":{"type":"array","items":{"$ref":"#/components/schemas/WalletSwapPairDto"}}}}},"400":{"description":"Invalid request, or the referenced entity does not exist or is not accessible","content":{"application/json":{"schema":{"$ref":"#/components/schemas/ErrorResponse"}}}},"401":{"description":"The access token is missing, expired or invalid, or the caller is not allowed to do this","content":{"application/json":{"schema":{"$ref":"#/components/schemas/ErrorResponse"}}}},"500":{"description":"Unexpected server error","content":{"application/json":{"schema":{"$ref":"#/components/schemas/ErrorResponse"}}}}}}},"/api/wallet-limits":{"get":{"tags":["Wallets"],"summary":"Get the caller's sending limits","description":"Returns, in US dollars, the most one withdrawal or transfer to another member may be worth, the limit for the window and what is used and left in it, the amount above which biometric confirmation is needed, and, while the calling device is in its waiting period after a sign-in or a change of its biometric key, when it may send again.","operationId":"getLimits","responses":{"200":{"description":"OK","content":{"*/*":{"schema":{"$ref":"#/components/schemas/WalletLimitsDto"}}}},"400":{"description":"Invalid request, or the referenced entity does not exist or is not accessible","content":{"application/json":{"schema":{"$ref":"#/components/schemas/ErrorResponse"}}}},"401":{"description":"The access token is missing, expired or invalid, or the caller is not allowed to do this","content":{"application/json":{"schema":{"$ref":"#/components/schemas/ErrorResponse"}}}},"500":{"description":"Unexpected server error","content":{"application/json":{"schema":{"$ref":"#/components/schemas/ErrorResponse"}}}}}}},"/api/wallet-limits/request":{"get":{"tags":["Wallets"],"summary":"Get what higher sending limits the caller may ask for","description":"Returns the tiers that may be asked for (the last is no limit at all), the durations, the caller's open request or the latest one decided within the last 7 days, and, after a refusal, from when a new request may be made.","operationId":"getRequestState","responses":{"200":{"description":"OK","content":{"*/*":{"schema":{"$ref":"#/components/schemas/WalletLimitRequestStateDto"}}}},"400":{"description":"Invalid request, or the referenced entity does not exist or is not accessible","content":{"application/json":{"schema":{"$ref":"#/components/schemas/ErrorResponse"}}}},"401":{"description":"The access token is missing, expired or invalid, or the caller is not allowed to do this","content":{"application/json":{"schema":{"$ref":"#/components/schemas/ErrorResponse"}}}},"500":{"description":"Unexpected server error","content":{"application/json":{"schema":{"$ref":"#/components/schemas/ErrorResponse"}}}}}}},"/api/wallet-balances":{"get":{"tags":["Wallets"],"summary":"List the balances of the caller's wallets","description":"Returns the current balance of every wallet the caller owns, ordered by wallet name. Answers 400 if a wallet has no balance record.","operationId":"getWalletBalances","responses":{"200":{"description":"OK","content":{"*/*":{"schema":{"type":"array","items":{"$ref":"#/components/schemas/WalletBalanceDto"}}}}},"400":{"description":"Invalid request, or the referenced entity does not exist or is not accessible","content":{"application/json":{"schema":{"$ref":"#/components/schemas/ErrorResponse"}}}},"401":{"description":"The access token is missing, expired or invalid, or the caller is not allowed to do this","content":{"application/json":{"schema":{"$ref":"#/components/schemas/ErrorResponse"}}}},"500":{"description":"Unexpected server error","content":{"application/json":{"schema":{"$ref":"#/components/schemas/ErrorResponse"}}}}}}},"/api/users":{"get":{"tags":["Users"],"summary":"Get the caller's profile","description":"Returns the profile of the authenticated user.","operationId":"getMe","responses":{"200":{"description":"OK","content":{"*/*":{"schema":{"$ref":"#/components/schemas/UserDto"}}}},"400":{"description":"Invalid request, or the referenced entity does not exist or is not accessible","content":{"application/json":{"schema":{"$ref":"#/components/schemas/ErrorResponse"}}}},"401":{"description":"The access token is missing, expired or invalid, or the caller is not allowed to do this","content":{"application/json":{"schema":{"$ref":"#/components/schemas/ErrorResponse"}}}},"500":{"description":"Unexpected server error","content":{"application/json":{"schema":{"$ref":"#/components/schemas/ErrorResponse"}}}}}}},"/api/users/{userId}/profile":{"get":{"tags":["Users"],"summary":"Get another user's public profile","description":"Returns when the user joined and how active they are in public rooms: posts and comments in public rooms and the number of public groups they are in. Nothing about private rooms or direct chats is included, since any signed-in user may ask. Answers 404, with no body, when the user does not exist, when either user has blocked the other, or when the account is disabled, so the answer does not reveal which.","operationId":"getUserProfile","parameters":[{"name":"userId","in":"path","description":"Id of the user","required":true,"schema":{"type":"string"}}],"responses":{"200":{"description":"The user's public profile","content":{"*/*":{"schema":{"$ref":"#/components/schemas/UserProfileDto"}}}},"404":{"description":"No such user, blocked either way, or disabled","content":{"application/json":{"schema":{"$ref":"#/components/schemas/ErrorResponse"}}}},"400":{"description":"Invalid request, or the referenced entity does not exist or is not accessible","content":{"application/json":{"schema":{"$ref":"#/components/schemas/ErrorResponse"}}}},"401":{"description":"The access token is missing, expired or invalid, or the caller is not allowed to do this","content":{"application/json":{"schema":{"$ref":"#/components/schemas/ErrorResponse"}}}},"500":{"description":"Unexpected server error","content":{"application/json":{"schema":{"$ref":"#/components/schemas/ErrorResponse"}}}}}}},"/api/users/qr-code":{"get":{"tags":["Users"],"summary":"Get the caller's QR code","description":"Returns a QR code that encodes a link identifying the caller, for another user's app to scan.","operationId":"generateUserQrCodeLink","responses":{"200":{"description":"The image, as PNG","content":{"image/png":{"schema":{"type":"string","format":"binary"}}}},"400":{"description":"Invalid request, or the referenced entity does not exist or is not accessible","content":{"application/json":{"schema":{"$ref":"#/components/schemas/ErrorResponse"}}}},"401":{"description":"The access token is missing, expired or invalid, or the caller is not allowed to do this","content":{"application/json":{"schema":{"$ref":"#/components/schemas/ErrorResponse"}}}},"500":{"description":"Unexpected server error","content":{"application/json":{"schema":{"$ref":"#/components/schemas/ErrorResponse"}}}}}}},"/api/users/connections":{"get":{"tags":["Contacts"],"summary":"List the caller's connections","description":"Returns one page of the users the caller is connected to, most recent connection first.","operationId":"getUserConnections","parameters":[{"name":"pageSize","in":"query","description":"Connections per page","required":false,"schema":{"type":"integer","format":"int32","default":10000}},{"name":"currentPage","in":"query","description":"Zero-based page number","required":false,"schema":{"type":"integer","format":"int32","default":0}}],"responses":{"200":{"description":"OK","content":{"*/*":{"schema":{"$ref":"#/components/schemas/PagedModelUserMinimalDto"}}}},"400":{"description":"Invalid request, or the referenced entity does not exist or is not accessible","content":{"application/json":{"schema":{"$ref":"#/components/schemas/ErrorResponse"}}}},"401":{"description":"The access token is missing, expired or invalid, or the caller is not allowed to do this","content":{"application/json":{"schema":{"$ref":"#/components/schemas/ErrorResponse"}}}},"500":{"description":"Unexpected server error","content":{"application/json":{"schema":{"$ref":"#/components/schemas/ErrorResponse"}}}}}}},"/api/users/connections/details/{userOfInterestId}":{"get":{"tags":["Contacts"],"summary":"Get a connected user's name","description":"Returns the id and name of a user the caller is connected to, or of the caller. Answers 400 for anyone else, so it cannot be used to look up strangers.","operationId":"getConnectedUserDetails","parameters":[{"name":"userOfInterestId","in":"path","description":"Id of the user","required":true,"schema":{"type":"string"}}],"responses":{"200":{"description":"OK","content":{"*/*":{"schema":{"$ref":"#/components/schemas/UserShortDto"}}}},"400":{"description":"Invalid request, or the referenced entity does not exist or is not accessible","content":{"application/json":{"schema":{"$ref":"#/components/schemas/ErrorResponse"}}}},"401":{"description":"The access token is missing, expired or invalid, or the caller is not allowed to do this","content":{"application/json":{"schema":{"$ref":"#/components/schemas/ErrorResponse"}}}},"500":{"description":"Unexpected server error","content":{"application/json":{"schema":{"$ref":"#/components/schemas/ErrorResponse"}}}}}}},"/api/users/connection-requests/incoming":{"get":{"tags":["Contacts"],"summary":"List the connection requests sent to the caller","description":"Returns the latest open connection requests addressed to the caller's email address or phone number, newest first. Only the first page is returned.","operationId":"getUserConnectionRequestsIncomingByUserId","responses":{"200":{"description":"OK","content":{"*/*":{"schema":{"$ref":"#/components/schemas/PagedModelUserConnectionRequestDto"}}}},"400":{"description":"Invalid request, or the referenced entity does not exist or is not accessible","content":{"application/json":{"schema":{"$ref":"#/components/schemas/ErrorResponse"}}}},"401":{"description":"The access token is missing, expired or invalid, or the caller is not allowed to do this","content":{"application/json":{"schema":{"$ref":"#/components/schemas/ErrorResponse"}}}},"500":{"description":"Unexpected server error","content":{"application/json":{"schema":{"$ref":"#/components/schemas/ErrorResponse"}}}}}}},"/api/users/2fa/2fa-qr-code":{"get":{"tags":["Users"],"summary":"Get the two-factor setup QR code","description":"Returns a QR code with the `otpauth://` link to add the account to an authenticator app. Only produced while two-factor authentication is not enabled; once it is, the secret is never shown again.","operationId":"generate2FaQrCodeImage","responses":{"200":{"description":"The image, as PNG","content":{"image/png":{"schema":{"type":"string","format":"binary"}}}},"400":{"description":"Invalid request, or the referenced entity does not exist or is not accessible","content":{"application/json":{"schema":{"$ref":"#/components/schemas/ErrorResponse"}}}},"401":{"description":"The access token is missing, expired or invalid, or the caller is not allowed to do this","content":{"application/json":{"schema":{"$ref":"#/components/schemas/ErrorResponse"}}}},"500":{"description":"Unexpected server error","content":{"application/json":{"schema":{"$ref":"#/components/schemas/ErrorResponse"}}}}}}},"/api/rooms/{roomId}/qr-code":{"get":{"tags":["Rooms"],"summary":"Get a room's QR code","description":"Returns a QR code that encodes a link to the room, for another user's app to scan and join. Available under the same rules as getting the room, and not for a direct chat between two users (400).","operationId":"generateRoomQrCodeLink","parameters":[{"name":"roomId","in":"path","description":"Id of the room","required":true,"schema":{"type":"string"}}],"responses":{"200":{"description":"The image, as PNG","content":{"image/png":{"schema":{"type":"string","format":"binary"}}}},"400":{"description":"Invalid request, or the referenced entity does not exist or is not accessible","content":{"application/json":{"schema":{"$ref":"#/components/schemas/ErrorResponse"}}}},"401":{"description":"The access token is missing, expired or invalid, or the caller is not allowed to do this","content":{"application/json":{"schema":{"$ref":"#/components/schemas/ErrorResponse"}}}},"500":{"description":"Unexpected server error","content":{"application/json":{"schema":{"$ref":"#/components/schemas/ErrorResponse"}}}}}}},"/api/rooms/{roomId}/extended":{"get":{"tags":["Rooms"],"summary":"Get a room with its participants and messages","description":"Returns what the room screen needs in one call: the room, the caller's own access settings for it, the participants by user id and a page of the latest messages. Answers 404 when the room does not exist or the caller cannot see it.","operationId":"getRoomDetailsExtended","parameters":[{"name":"roomId","in":"path","description":"Id of the room","required":true,"schema":{"type":"string"}}],"responses":{"200":{"description":"OK","content":{"*/*":{"schema":{"$ref":"#/components/schemas/RoomDetailsDto"}}}},"400":{"description":"Invalid request, or the referenced entity does not exist or is not accessible","content":{"application/json":{"schema":{"$ref":"#/components/schemas/ErrorResponse"}}}},"401":{"description":"The access token is missing, expired or invalid, or the caller is not allowed to do this","content":{"application/json":{"schema":{"$ref":"#/components/schemas/ErrorResponse"}}}},"500":{"description":"Unexpected server error","content":{"application/json":{"schema":{"$ref":"#/components/schemas/ErrorResponse"}}}}}}},"/api/rooms/resolve-slug/{key}":{"get":{"tags":["Rooms"],"summary":"Resolve a public room link","description":"Resolves the key of a shared `/r/{key}` link, a room's readable slug (current or earlier, in any case) or its id, to the room it points at. Needs no access token, so a tap on a shared link works before signing in, and returns only what identifies the room, never messages.","operationId":"resolveSlug","parameters":[{"name":"key","in":"path","description":"The slug or room id from the link","required":true,"schema":{"type":"string"}}],"responses":{"200":{"description":"OK","content":{"*/*":{"schema":{"$ref":"#/components/schemas/Resolution"}}}},"400":{"description":"Invalid request, or the referenced entity does not exist or is not accessible","content":{"application/json":{"schema":{"$ref":"#/components/schemas/ErrorResponse"}}}},"500":{"description":"Unexpected server error","content":{"application/json":{"schema":{"$ref":"#/components/schemas/ErrorResponse"}}}}},"security":[]}},"/api/rooms/public-name-availability":{"get":{"tags":["Rooms"],"summary":"Check a public room name","description":"Tells whether a public room can be created with, or renamed to, the given name, and which readable link the name maps to. A name is unavailable when its link is reserved or has ever been used by another room.","operationId":"checkPublicRoomNameAvailability","parameters":[{"name":"name","in":"query","description":"The room name to check","required":true,"schema":{"type":"string"}},{"name":"roomId","in":"query","description":"Id of the room being renamed, so that its own links count as available; omit when creating a room","required":false,"schema":{"type":["string","null"]}}],"responses":{"200":{"description":"OK","content":{"*/*":{"schema":{"$ref":"#/components/schemas/SlugCheck"}}}},"400":{"description":"Invalid request, or the referenced entity does not exist or is not accessible","content":{"application/json":{"schema":{"$ref":"#/components/schemas/ErrorResponse"}}}},"401":{"description":"The access token is missing, expired or invalid, or the caller is not allowed to do this","content":{"application/json":{"schema":{"$ref":"#/components/schemas/ErrorResponse"}}}},"500":{"description":"Unexpected server error","content":{"application/json":{"schema":{"$ref":"#/components/schemas/ErrorResponse"}}}}}}},"/api/payment-requests/{paymentRequestId}":{"get":{"tags":["Wallets"],"summary":"Get a payment request","description":"Returns a payment request with its items. Visible to the owner of the target wallet and to the recipients of its items.","operationId":"getPaymentRequest","parameters":[{"name":"paymentRequestId","in":"path","description":"Id of the payment request","required":true,"schema":{"type":"string"}}],"responses":{"200":{"description":"OK","content":{"*/*":{"schema":{"$ref":"#/components/schemas/PaymentRequestDto"}}}},"400":{"description":"Invalid request, or the referenced entity does not exist or is not accessible","content":{"application/json":{"schema":{"$ref":"#/components/schemas/ErrorResponse"}}}},"401":{"description":"The access token is missing, expired or invalid, or the caller is not allowed to do this","content":{"application/json":{"schema":{"$ref":"#/components/schemas/ErrorResponse"}}}},"500":{"description":"Unexpected server error","content":{"application/json":{"schema":{"$ref":"#/components/schemas/ErrorResponse"}}}}}}},"/api/payment-requests/wallet/{targetWalletId}":{"get":{"tags":["Wallets"],"summary":"List the payment requests of a wallet","description":"Returns one page of the payment requests that pay into one of the caller's wallets, newest first, with their items.","operationId":"getPaymentRequestsForWallet","parameters":[{"name":"targetWalletId","in":"path","description":"Id of the wallet the requests pay into","required":true,"schema":{"type":"string"}},{"name":"types","in":"query","description":"Only include requests in these payment states (Paid, PartiallyPaid, Unpaid, Cancelled); all when omitted","required":false,"schema":{"type":"array","items":{"type":"string","enum":["Paid","PartiallyPaid","Unpaid","Cancelled"]}}},{"name":"pageSize","in":"query","description":"Requests per page","required":false,"schema":{"type":"integer","format":"int32","default":10}},{"name":"currentPage","in":"query","description":"Zero-based page number","required":false,"schema":{"type":"integer","format":"int32","default":0}}],"responses":{"200":{"description":"OK","content":{"*/*":{"schema":{"$ref":"#/components/schemas/PagedModelPaymentRequestDto"}}}},"400":{"description":"Invalid request, or the referenced entity does not exist or is not accessible","content":{"application/json":{"schema":{"$ref":"#/components/schemas/ErrorResponse"}}}},"401":{"description":"The access token is missing, expired or invalid, or the caller is not allowed to do this","content":{"application/json":{"schema":{"$ref":"#/components/schemas/ErrorResponse"}}}},"500":{"description":"Unexpected server error","content":{"application/json":{"schema":{"$ref":"#/components/schemas/ErrorResponse"}}}}}}},"/api/payment-requests/items/{itemId}":{"get":{"tags":["Wallets"],"summary":"Get the payment request of an item","description":"Returns the payment request a payment request item belongs to. Visible to the owner of the target wallet and to the item's recipient.","operationId":"getPaymentRequestForItem","parameters":[{"name":"itemId","in":"path","description":"Id of the payment request item","required":true,"schema":{"type":"string"}}],"responses":{"200":{"description":"OK","content":{"*/*":{"schema":{"$ref":"#/components/schemas/PaymentRequestDto"}}}},"400":{"description":"Invalid request, or the referenced entity does not exist or is not accessible","content":{"application/json":{"schema":{"$ref":"#/components/schemas/ErrorResponse"}}}},"401":{"description":"The access token is missing, expired or invalid, or the caller is not allowed to do this","content":{"application/json":{"schema":{"$ref":"#/components/schemas/ErrorResponse"}}}},"500":{"description":"Unexpected server error","content":{"application/json":{"schema":{"$ref":"#/components/schemas/ErrorResponse"}}}}}}},"/api/payment-requests/items/{itemId}/qr-code":{"get":{"tags":["Wallets"],"summary":"Get the QR code of a payment request item","description":"Returns a QR code that encodes a link to a payment request item, for the payer to scan. Visible to the owner of the target wallet and to the item's recipient.","operationId":"getPaymentRequestItemQrCode","parameters":[{"name":"itemId","in":"path","description":"Id of the payment request item","required":true,"schema":{"type":"string"}}],"responses":{"200":{"description":"The image, as PNG","content":{"image/png":{"schema":{"type":"string","format":"binary"}}}},"400":{"description":"Invalid request, or the referenced entity does not exist or is not accessible","content":{"application/json":{"schema":{"$ref":"#/components/schemas/ErrorResponse"}}}},"401":{"description":"The access token is missing, expired or invalid, or the caller is not allowed to do this","content":{"application/json":{"schema":{"$ref":"#/components/schemas/ErrorResponse"}}}},"500":{"description":"Unexpected server error","content":{"application/json":{"schema":{"$ref":"#/components/schemas/ErrorResponse"}}}}}}},"/api/payment-requests/items/mine":{"get":{"tags":["Wallets"],"summary":"List the payments the caller is asked to make","description":"Returns one page of the payment request items in which the caller is the person asked to pay, newest first.","operationId":"getMyPaymentRequestItems","parameters":[{"name":"pageSize","in":"query","description":"Items per page","required":false,"schema":{"type":"integer","format":"int32","default":10}},{"name":"currentPage","in":"query","description":"Zero-based page number","required":false,"schema":{"type":"integer","format":"int32","default":0}}],"responses":{"200":{"description":"OK","content":{"*/*":{"schema":{"$ref":"#/components/schemas/PagedModelPaymentRequestItemDto"}}}},"400":{"description":"Invalid request, or the referenced entity does not exist or is not accessible","content":{"application/json":{"schema":{"$ref":"#/components/schemas/ErrorResponse"}}}},"401":{"description":"The access token is missing, expired or invalid, or the caller is not allowed to do this","content":{"application/json":{"schema":{"$ref":"#/components/schemas/ErrorResponse"}}}},"500":{"description":"Unexpected server error","content":{"application/json":{"schema":{"$ref":"#/components/schemas/ErrorResponse"}}}}}}},"/api/messages/{messageId}/attachments/{attachmentId}":{"get":{"tags":["Messages"],"summary":"Download an attachment","description":"Returns the file of an attachment, or its thumbnail. Audio and video are streamed and honour the `Range` request header (answering 206 for a partial range) unless `download` is true. The caller must have access to the message's room. An end-to-end encrypted attachment is returned as the opaque encrypted blob.","operationId":"retrieveMessageAttachment","parameters":[{"name":"messageId","in":"path","description":"Id of the message","required":true,"schema":{"type":"string"}},{"name":"attachmentId","in":"path","description":"Id of the attachment","required":true,"schema":{"type":"string"}},{"name":"download","in":"query","description":"Send the file as a download instead of streaming audio and video","required":false,"schema":{"type":"boolean","default":false}},{"name":"thumbnail","in":"query","description":"Return the thumbnail instead of the file","required":false,"schema":{"type":"boolean","default":false}}],"responses":{"200":{"description":"The file, or its thumbnail","content":{"application/octet-stream":{"schema":{"type":"string","format":"binary"}}}},"400":{"description":"Invalid request, or the referenced entity does not exist or is not accessible","content":{"application/json":{"schema":{"$ref":"#/components/schemas/ErrorResponse"}}}},"401":{"description":"The access token is missing, expired or invalid, or the caller is not allowed to do this","content":{"application/json":{"schema":{"$ref":"#/components/schemas/ErrorResponse"}}}},"500":{"description":"Unexpected server error","content":{"application/json":{"schema":{"$ref":"#/components/schemas/ErrorResponse"}}}}}}},"/api/messages/{messageId}/attachments/{attachmentId}/ticketed":{"get":{"tags":["Messages"],"summary":"Download an attachment with a ticket","description":"The same as downloading an attachment, for a link from `POST /api/messages/{messageId}/attachments/{attachmentId}/ticket`: no `Authorization` header is needed, the ticket in the link stands for the user it was issued to. The ticket must be unexpired and issued for this message and attachment, and that user must still have access to the room; anything else is answered 401.","operationId":"retrieveMessageAttachmentWithTicket","parameters":[{"name":"messageId","in":"path","description":"Id of the message","required":true,"schema":{"type":"string"}},{"name":"attachmentId","in":"path","description":"Id of the attachment","required":true,"schema":{"type":"string"}},{"name":"ticket","in":"query","description":"The ticket from the link","required":true,"schema":{"type":"string"}},{"name":"download","in":"query","description":"Send the file as a download instead of streaming audio and video","required":false,"schema":{"type":"boolean","default":false}},{"name":"thumbnail","in":"query","description":"Return the thumbnail instead of the file","required":false,"schema":{"type":"boolean","default":false}}],"responses":{"200":{"description":"The file, or its thumbnail","content":{"application/octet-stream":{"schema":{"type":"string","format":"binary"}}}},"400":{"description":"Invalid request, or the referenced entity does not exist or is not accessible","content":{"application/json":{"schema":{"$ref":"#/components/schemas/ErrorResponse"}}}},"500":{"description":"Unexpected server error","content":{"application/json":{"schema":{"$ref":"#/components/schemas/ErrorResponse"}}}}},"security":[]}},"/api/messages/{messageId}/attachments/details":{"get":{"tags":["Messages"],"summary":"List the attachments of a message","description":"Returns the file name, size and type of each attachment of a message. The caller must have access to the message's room. Answers 400 when the message has no attachments.","operationId":"getMessageAttachmentsDetails","parameters":[{"name":"messageId","in":"path","description":"Id of the message","required":true,"schema":{"type":"string"}}],"responses":{"200":{"description":"OK","content":{"*/*":{"schema":{"type":"array","items":{"$ref":"#/components/schemas/RoomMessageAttachmentDto"}}}}},"400":{"description":"Invalid request, or the referenced entity does not exist or is not accessible","content":{"application/json":{"schema":{"$ref":"#/components/schemas/ErrorResponse"}}}},"401":{"description":"The access token is missing, expired or invalid, or the caller is not allowed to do this","content":{"application/json":{"schema":{"$ref":"#/components/schemas/ErrorResponse"}}}},"500":{"description":"Unexpected server error","content":{"application/json":{"schema":{"$ref":"#/components/schemas/ErrorResponse"}}}}}}},"/api/messages/byRoom/{roomId}":{"get":{"tags":["Messages"],"summary":"List a room's messages","description":"Returns one page of the room's messages, newest first (highest `messageIndex` first), leaving out messages addressed to other users. The caller must be an enabled member of the room. The exception is a broadcast room, which every user can read once it is visible to clients; its posts come back in the caller's language.","operationId":"fetchOlderMessages","parameters":[{"name":"roomId","in":"path","description":"Id of the room","required":true,"schema":{"type":"string"}},{"name":"pageNumber","in":"query","description":"Zero-based page number","required":true,"schema":{"type":"integer","format":"int32"}},{"name":"pageSize","in":"query","description":"Messages per page","required":false,"schema":{"type":"integer","format":"int32","default":20}}],"responses":{"200":{"description":"OK","content":{"*/*":{"schema":{"$ref":"#/components/schemas/PagedModelRoomMessageDto"}}}},"400":{"description":"Invalid request, or the referenced entity does not exist or is not accessible","content":{"application/json":{"schema":{"$ref":"#/components/schemas/ErrorResponse"}}}},"401":{"description":"The access token is missing, expired or invalid, or the caller is not allowed to do this","content":{"application/json":{"schema":{"$ref":"#/components/schemas/ErrorResponse"}}}},"500":{"description":"Unexpected server error","content":{"application/json":{"schema":{"$ref":"#/components/schemas/ErrorResponse"}}}}}}},"/api/geo-location":{"get":{"tags":["Utilities"],"summary":"Look up the caller's location","description":"Resolves the latitude and longitude of the caller's IP address, read from `X-Forwarded-For` through our own proxies only. Answers 400 when the address cannot be located.","operationId":"getGeoLocation","responses":{"200":{"description":"OK","content":{"*/*":{"schema":{"$ref":"#/components/schemas/GeoLocation"}}}},"400":{"description":"Invalid request, or the referenced entity does not exist or is not accessible","content":{"application/json":{"schema":{"$ref":"#/components/schemas/ErrorResponse"}}}},"401":{"description":"The access token is missing, expired or invalid, or the caller is not allowed to do this","content":{"application/json":{"schema":{"$ref":"#/components/schemas/ErrorResponse"}}}},"500":{"description":"Unexpected server error","content":{"application/json":{"schema":{"$ref":"#/components/schemas/ErrorResponse"}}}}}}},"/api/e2ee/rooms/{roomId}/missing":{"get":{"tags":["End-to-end encryption"],"summary":"List the devices missing room keys","description":"Returns the eligible devices of the room's active members that lack the key for some versions, so that a device holding those versions can upload the missing shares. One entry per device with at least one missing version.","operationId":"findMissingShares","parameters":[{"name":"roomId","in":"path","description":"Id of the end-to-end encrypted room","required":true,"schema":{"type":"string"}}],"responses":{"200":{"description":"The devices and the key versions each lacks","content":{"*/*":{"schema":{"type":"array","items":{"$ref":"#/components/schemas/E2eeMissingShareDto"}}}}},"429":{"description":"Too many calls; try again shortly","content":{"application/json":{"schema":{"$ref":"#/components/schemas/ErrorResponse"}}}},"400":{"description":"Invalid request, or the referenced entity does not exist or is not accessible","content":{"application/json":{"schema":{"$ref":"#/components/schemas/ErrorResponse"}}}},"401":{"description":"The access token is missing, expired or invalid, or the caller is not allowed to do this","content":{"application/json":{"schema":{"$ref":"#/components/schemas/ErrorResponse"}}}},"500":{"description":"Unexpected server error","content":{"application/json":{"schema":{"$ref":"#/components/schemas/ErrorResponse"}}}}}}},"/api/e2ee/rooms/{roomId}/devices":{"get":{"tags":["End-to-end encryption"],"summary":"List the devices a room key is wrapped for","description":"Returns every device the room's key may currently be wrapped for: the eligible devices of all its active members. A device creating a key version should use this rather than its own, possibly stale, member list. The caller must be an active member; answers 400 for a room that is not end-to-end encrypted.","operationId":"listRoomDevices","parameters":[{"name":"roomId","in":"path","description":"Id of the end-to-end encrypted room","required":true,"schema":{"type":"string"}}],"responses":{"200":{"description":"The devices","content":{"*/*":{"schema":{"type":"array","items":{"$ref":"#/components/schemas/E2eeDeviceDto"}}}}},"429":{"description":"Too many calls; try again shortly","content":{"application/json":{"schema":{"$ref":"#/components/schemas/ErrorResponse"}}}},"400":{"description":"Invalid request, or the referenced entity does not exist or is not accessible","content":{"application/json":{"schema":{"$ref":"#/components/schemas/ErrorResponse"}}}},"401":{"description":"The access token is missing, expired or invalid, or the caller is not allowed to do this","content":{"application/json":{"schema":{"$ref":"#/components/schemas/ErrorResponse"}}}},"500":{"description":"Unexpected server error","content":{"application/json":{"schema":{"$ref":"#/components/schemas/ErrorResponse"}}}}}}},"/api/e2ee/devices/{userId}":{"get":{"tags":["End-to-end encryption"],"summary":"List a user's devices that can receive room keys","description":"Returns the devices of a user that room keys may be wrapped for, most recently seen first. Only the user themselves and users who share an active room with them get an answer; for anyone else the list is empty, the same as for a user with no devices, so this cannot be used to probe for accounts.","operationId":"listDevices","parameters":[{"name":"userId","in":"path","description":"Id of the user","required":true,"schema":{"type":"string"}}],"responses":{"200":{"description":"The devices; empty when the caller may not see them","content":{"*/*":{"schema":{"type":"array","items":{"$ref":"#/components/schemas/E2eeDeviceDto"}}}}},"429":{"description":"Too many calls; try again shortly","content":{"application/json":{"schema":{"$ref":"#/components/schemas/ErrorResponse"}}}},"400":{"description":"Invalid request, or the referenced entity does not exist or is not accessible","content":{"application/json":{"schema":{"$ref":"#/components/schemas/ErrorResponse"}}}},"401":{"description":"The access token is missing, expired or invalid, or the caller is not allowed to do this","content":{"application/json":{"schema":{"$ref":"#/components/schemas/ErrorResponse"}}}},"500":{"description":"Unexpected server error","content":{"application/json":{"schema":{"$ref":"#/components/schemas/ErrorResponse"}}}}}}},"/api/devices":{"get":{"tags":["Devices"],"summary":"List the caller's devices","description":"Returns the devices registered to the caller, most recently seen first, including revoked ones. Marks which one the request came from, and says where each device's last IP address places it, in the caller's language.","operationId":"listDevices_1","responses":{"200":{"description":"OK","content":{"*/*":{"schema":{"type":"array","items":{"$ref":"#/components/schemas/DeviceSummaryDto"}}}}},"400":{"description":"Invalid request, or the referenced entity does not exist or is not accessible","content":{"application/json":{"schema":{"$ref":"#/components/schemas/ErrorResponse"}}}},"401":{"description":"The access token is missing, expired or invalid, or the caller is not allowed to do this","content":{"application/json":{"schema":{"$ref":"#/components/schemas/ErrorResponse"}}}},"500":{"description":"Unexpected server error","content":{"application/json":{"schema":{"$ref":"#/components/schemas/ErrorResponse"}}}}}}},"/api/crypto-exchange-rates":{"get":{"tags":["Wallets"],"summary":"Get the crypto exchange rates","description":"Returns the current exchange-rate table: fiat currency code to the price of one unit of the cryptocurrency, as used to show wallet balances in the currency chosen in the user settings.","operationId":"fetchCryptoExchangeRates","responses":{"200":{"description":"OK","content":{"*/*":{"schema":{"type":"object","additionalProperties":{"type":"number","format":"double"}}}}},"400":{"description":"Invalid request, or the referenced entity does not exist or is not accessible","content":{"application/json":{"schema":{"$ref":"#/components/schemas/ErrorResponse"}}}},"401":{"description":"The access token is missing, expired or invalid, or the caller is not allowed to do this","content":{"application/json":{"schema":{"$ref":"#/components/schemas/ErrorResponse"}}}},"500":{"description":"Unexpected server error","content":{"application/json":{"schema":{"$ref":"#/components/schemas/ErrorResponse"}}}}}}},"/api/chat/status":{"get":{"tags":["Rooms"],"summary":"Get the chat status","description":"Returns the snapshot the app loads on start: the caller's profile and settings, their rooms, the unread message count per room, their contacts, the available chat backgrounds and whether the Bitcoin and Ethereum main networks are in use.","operationId":"getChatStatus","responses":{"200":{"description":"OK","content":{"*/*":{"schema":{"$ref":"#/components/schemas/ChatStatusDto"}}}},"400":{"description":"Invalid request, or the referenced entity does not exist or is not accessible","content":{"application/json":{"schema":{"$ref":"#/components/schemas/ErrorResponse"}}}},"401":{"description":"The access token is missing, expired or invalid, or the caller is not allowed to do this","content":{"application/json":{"schema":{"$ref":"#/components/schemas/ErrorResponse"}}}},"500":{"description":"Unexpected server error","content":{"application/json":{"schema":{"$ref":"#/components/schemas/ErrorResponse"}}}}}}},"/api/calls/{callId}":{"get":{"tags":["Calls"],"summary":"Get a call","description":"Returns the current state of a call. Only its caller and callee may read it.","operationId":"getCall","parameters":[{"name":"callId","in":"path","description":"Id of the call","required":true,"schema":{"type":"string"}}],"responses":{"200":{"description":"OK","content":{"*/*":{"schema":{"$ref":"#/components/schemas/CallResponse"}}}},"400":{"description":"Invalid request, or the referenced entity does not exist or is not accessible","content":{"application/json":{"schema":{"$ref":"#/components/schemas/ErrorResponse"}}}},"401":{"description":"The access token is missing, expired or invalid, or the caller is not allowed to do this","content":{"application/json":{"schema":{"$ref":"#/components/schemas/ErrorResponse"}}}},"500":{"description":"Unexpected server error","content":{"application/json":{"schema":{"$ref":"#/components/schemas/ErrorResponse"}}}}}}},"/api/calls/ice-config":{"get":{"tags":["Calls"],"summary":"Get the ICE server configuration","description":"Returns the STUN and TURN servers a WebRTC connection should use, with TURN credentials generated for the caller.","operationId":"getIceConfig","responses":{"200":{"description":"OK","content":{"*/*":{"schema":{"$ref":"#/components/schemas/IceConfigResponse"}}}},"400":{"description":"Invalid request, or the referenced entity does not exist or is not accessible","content":{"application/json":{"schema":{"$ref":"#/components/schemas/ErrorResponse"}}}},"401":{"description":"The access token is missing, expired or invalid, or the caller is not allowed to do this","content":{"application/json":{"schema":{"$ref":"#/components/schemas/ErrorResponse"}}}},"500":{"description":"Unexpected server error","content":{"application/json":{"schema":{"$ref":"#/components/schemas/ErrorResponse"}}}}}}},"/api/avatars/wallet/{walletId}":{"get":{"tags":["Users"],"summary":"Get a wallet's avatar","description":"Returns the generated avatar of a wallet. Sent with a one-day `Cache-Control`.","operationId":"getAvatarByWalletId","parameters":[{"name":"walletId","in":"path","description":"Id of the wallet","required":true,"schema":{"type":"string"}}],"responses":{"200":{"description":"The image, as PNG","content":{"image/png":{"schema":{"type":"string","format":"binary"}}}},"400":{"description":"Invalid request, or the referenced entity does not exist or is not accessible","content":{"application/json":{"schema":{"$ref":"#/components/schemas/ErrorResponse"}}}},"401":{"description":"The access token is missing, expired or invalid, or the caller is not allowed to do this","content":{"application/json":{"schema":{"$ref":"#/components/schemas/ErrorResponse"}}}},"500":{"description":"Unexpected server error","content":{"application/json":{"schema":{"$ref":"#/components/schemas/ErrorResponse"}}}}}}},"/api/avatars/user/{userId}":{"get":{"tags":["Users"],"summary":"Get a user's avatar","description":"Returns the avatar of any user. Sent with `Cache-Control: no-cache`, so clients revalidate it.","operationId":"getAvatarByUserId","parameters":[{"name":"userId","in":"path","description":"Id of the user","required":true,"schema":{"type":"string"}}],"responses":{"200":{"description":"The image, as PNG","content":{"image/png":{"schema":{"type":"string","format":"binary"}}}},"400":{"description":"Invalid request, or the referenced entity does not exist or is not accessible","content":{"application/json":{"schema":{"$ref":"#/components/schemas/ErrorResponse"}}}},"401":{"description":"The access token is missing, expired or invalid, or the caller is not allowed to do this","content":{"application/json":{"schema":{"$ref":"#/components/schemas/ErrorResponse"}}}},"500":{"description":"Unexpected server error","content":{"application/json":{"schema":{"$ref":"#/components/schemas/ErrorResponse"}}}}}}},"/api/avatars/user/{userId}/public":{"get":{"tags":["Users"],"summary":"Get a user's avatar without signing in","description":"Same image as the authenticated variant, for callers that have no access token, such as the push service rendering a call notification.","operationId":"getAvatarByUserIdPublic","parameters":[{"name":"userId","in":"path","description":"Id of the user","required":true,"schema":{"type":"string"}}],"responses":{"200":{"description":"The image, as PNG","content":{"image/png":{"schema":{"type":"string","format":"binary"}}}},"400":{"description":"Invalid request, or the referenced entity does not exist or is not accessible","content":{"application/json":{"schema":{"$ref":"#/components/schemas/ErrorResponse"}}}},"500":{"description":"Unexpected server error","content":{"application/json":{"schema":{"$ref":"#/components/schemas/ErrorResponse"}}}}},"security":[]}},"/api/apps/{appId}/payments/{paymentId}":{"get":{"tags":["Apps"],"summary":"Get one of the caller's payments to an app","description":"The payment as the sheet or the history shows it; a pending one carries the caller's balance before and after paying.","operationId":"getPayment","parameters":[{"name":"appId","in":"path","description":"Id of the app","required":true,"schema":{"type":"string"}},{"name":"paymentId","in":"path","description":"Id of the payment","required":true,"schema":{"type":"string"}}],"responses":{"200":{"description":"The payment","content":{"*/*":{"schema":{"$ref":"#/components/schemas/AppPaymentDto"}}}},"400":{"description":"No such payment of the caller","content":{"application/json":{"schema":{"$ref":"#/components/schemas/ErrorResponse"}}}},"401":{"description":"The access token is missing, expired or invalid, or the caller is not allowed to do this","content":{"application/json":{"schema":{"$ref":"#/components/schemas/ErrorResponse"}}}},"500":{"description":"Unexpected server error","content":{"application/json":{"schema":{"$ref":"#/components/schemas/ErrorResponse"}}}}}}},"/api/apps/signing-keys":{"get":{"tags":["Apps"],"summary":"Get the keys that verify what the server signs for apps","description":"A JSON Web Key Set with the Ed25519 public key(s) that verify cards and web-app session tokens signed by the server. Public: devices and developers' backends fetch it and may cache it for a day.","operationId":"signingKeys","responses":{"200":{"description":"The key set","content":{"*/*":{"schema":{"$ref":"#/components/schemas/JwksDto"}}}},"400":{"description":"Invalid request, or the referenced entity does not exist or is not accessible","content":{"application/json":{"schema":{"$ref":"#/components/schemas/ErrorResponse"}}}},"500":{"description":"Unexpected server error","content":{"application/json":{"schema":{"$ref":"#/components/schemas/ErrorResponse"}}}}},"security":[]}},"/api/apps/rooms/{roomId}":{"get":{"tags":["Apps"],"summary":"List the apps in a room","description":"The room bots a room's admin added, for the room's members.","operationId":"appsInRoom","parameters":[{"name":"roomId","in":"path","description":"Id of the room","required":true,"schema":{"type":"string"}}],"responses":{"200":{"description":"The apps, in the order they were added","content":{"*/*":{"schema":{"type":"array","items":{"$ref":"#/components/schemas/AppDto"}}}}},"400":{"description":"Invalid request, or the referenced entity does not exist or is not accessible","content":{"application/json":{"schema":{"$ref":"#/components/schemas/ErrorResponse"}}}},"401":{"description":"The access token is missing, expired or invalid, or the caller is not allowed to do this","content":{"application/json":{"schema":{"$ref":"#/components/schemas/ErrorResponse"}}}},"500":{"description":"Unexpected server error","content":{"application/json":{"schema":{"$ref":"#/components/schemas/ErrorResponse"}}}}}}},"/api/apps/payments/mine":{"get":{"tags":["Apps"],"summary":"List the caller's payments to and from apps","description":"Newest first; optionally only one app's.","operationId":"myPayments","parameters":[{"name":"appId","in":"query","description":"Only this app's payments","required":false,"schema":{"type":"string"}},{"name":"page","in":"query","description":"Page number, from 0","required":false,"schema":{"type":"integer","format":"int32","default":0}},{"name":"size","in":"query","description":"Page size, at most 50","required":false,"schema":{"type":"integer","format":"int32","default":20}}],"responses":{"200":{"description":"A page of payments","content":{"*/*":{"schema":{"$ref":"#/components/schemas/PagedModelAppPaymentDto"}}}},"400":{"description":"Invalid request, or the referenced entity does not exist or is not accessible","content":{"application/json":{"schema":{"$ref":"#/components/schemas/ErrorResponse"}}}},"401":{"description":"The access token is missing, expired or invalid, or the caller is not allowed to do this","content":{"application/json":{"schema":{"$ref":"#/components/schemas/ErrorResponse"}}}},"500":{"description":"Unexpected server error","content":{"application/json":{"schema":{"$ref":"#/components/schemas/ErrorResponse"}}}}}}},"/api/apps/installed":{"get":{"tags":["Apps"],"summary":"List the caller's apps","description":"Returns every app the caller has, with the manifest version their device should use: the published version, or the draft for the owner and testers of an app that has not published yet. Preinstalled first-party apps are included last. Devices cache this and refresh it when they receive the apps.changed event.","operationId":"listInstalled","responses":{"200":{"description":"The apps, oldest install first","content":{"*/*":{"schema":{"type":"array","items":{"$ref":"#/components/schemas/InstalledAppDto"}}}}},"400":{"description":"Invalid request, or the referenced entity does not exist or is not accessible","content":{"application/json":{"schema":{"$ref":"#/components/schemas/ErrorResponse"}}}},"401":{"description":"The access token is missing, expired or invalid, or the caller is not allowed to do this","content":{"application/json":{"schema":{"$ref":"#/components/schemas/ErrorResponse"}}}},"500":{"description":"Unexpected server error","content":{"application/json":{"schema":{"$ref":"#/components/schemas/ErrorResponse"}}}}}}},"/api/apps/directory":{"get":{"tags":["Apps"],"summary":"Search the public app directory","description":"Returns the approved public apps whose name or description contains the query, most installed first. An empty query lists them all.","operationId":"directory","parameters":[{"name":"q","in":"query","description":"Text to look for in names and descriptions; may be empty","required":false,"schema":{"type":"string","default":""}},{"name":"page","in":"query","description":"Page number, from 0","required":false,"schema":{"type":"integer","format":"int32","default":0}},{"name":"size","in":"query","description":"Page size, at most 50","required":false,"schema":{"type":"integer","format":"int32","default":20}}],"responses":{"200":{"description":"A page of apps","content":{"*/*":{"schema":{"$ref":"#/components/schemas/PagedModelAppDto"}}}},"400":{"description":"Invalid request, or the referenced entity does not exist or is not accessible","content":{"application/json":{"schema":{"$ref":"#/components/schemas/ErrorResponse"}}}},"401":{"description":"The access token is missing, expired or invalid, or the caller is not allowed to do this","content":{"application/json":{"schema":{"$ref":"#/components/schemas/ErrorResponse"}}}},"500":{"description":"Unexpected server error","content":{"application/json":{"schema":{"$ref":"#/components/schemas/ErrorResponse"}}}}}}},"/api/apps/by-slug/{slug}":{"get":{"tags":["Apps"],"summary":"Find an app by the slug in a link or QR code","description":"Returns the app if the caller may see it: it is active and either not private or the caller is its owner or a tester. Answers 400 otherwise, the same as for a slug that does not exist.","operationId":"bySlug","parameters":[{"name":"slug","in":"path","description":"The app's slug","required":true,"schema":{"type":"string"}}],"responses":{"200":{"description":"The app","content":{"*/*":{"schema":{"$ref":"#/components/schemas/AppDto"}}}},"400":{"description":"Invalid request, or the referenced entity does not exist or is not accessible","content":{"application/json":{"schema":{"$ref":"#/components/schemas/ErrorResponse"}}}},"401":{"description":"The access token is missing, expired or invalid, or the caller is not allowed to do this","content":{"application/json":{"schema":{"$ref":"#/components/schemas/ErrorResponse"}}}},"500":{"description":"Unexpected server error","content":{"application/json":{"schema":{"$ref":"#/components/schemas/ErrorResponse"}}}}}}},"/api/wallet-limits/requests/{requestId}":{"delete":{"tags":["Wallets"],"summary":"Cancel a request for higher sending limits","description":"Cancels the caller's own request while it is still open.","operationId":"cancelRequest","parameters":[{"name":"requestId","in":"path","description":"The request's id","required":true,"schema":{"type":"string"}}],"responses":{"200":{"description":"OK","content":{"*/*":{"schema":{"$ref":"#/components/schemas/WalletLimitRequestDto"}}}},"400":{"description":"Invalid request, or the referenced entity does not exist or is not accessible","content":{"application/json":{"schema":{"$ref":"#/components/schemas/ErrorResponse"}}}},"401":{"description":"The access token is missing, expired or invalid, or the caller is not allowed to do this","content":{"application/json":{"schema":{"$ref":"#/components/schemas/ErrorResponse"}}}},"500":{"description":"Unexpected server error","content":{"application/json":{"schema":{"$ref":"#/components/schemas/ErrorResponse"}}}}}}},"/api/users/connection-requests/{userConnectionRequestId}":{"delete":{"tags":["Contacts"],"summary":"Withdraw a connection request","description":"Deletes a connection request the caller sent. Always answers 204, also when the request does not exist or was sent by someone else, in which case nothing is deleted.","operationId":"deleteUserConnectionRequest","parameters":[{"name":"userConnectionRequestId","in":"path","description":"Id of the connection request, from the list of sent requests","required":true,"schema":{"type":"string"}}],"responses":{"204":{"description":"The request no longer exists"},"400":{"description":"Invalid request, or the referenced entity does not exist or is not accessible","content":{"application/json":{"schema":{"$ref":"#/components/schemas/ErrorResponse"}}}},"401":{"description":"The access token is missing, expired or invalid, or the caller is not allowed to do this","content":{"application/json":{"schema":{"$ref":"#/components/schemas/ErrorResponse"}}}},"500":{"description":"Unexpected server error","content":{"application/json":{"schema":{"$ref":"#/components/schemas/ErrorResponse"}}}}}}},"/api/users/blocks/{userBlockId}":{"delete":{"tags":["Users"],"summary":"Unblock a user","description":"Removes one of the caller's blocks. Always answers 204, also when the block does not exist or belongs to someone else, in which case nothing is removed.","operationId":"unblockUser","parameters":[{"name":"userBlockId","in":"path","description":"Id of the block, from the list of blocks","required":true,"schema":{"type":"string"}}],"responses":{"204":{"description":"The block no longer exists"},"400":{"description":"Invalid request, or the referenced entity does not exist or is not accessible","content":{"application/json":{"schema":{"$ref":"#/components/schemas/ErrorResponse"}}}},"401":{"description":"The access token is missing, expired or invalid, or the caller is not allowed to do this","content":{"application/json":{"schema":{"$ref":"#/components/schemas/ErrorResponse"}}}},"500":{"description":"Unexpected server error","content":{"application/json":{"schema":{"$ref":"#/components/schemas/ErrorResponse"}}}}}}},"/api/messages/{messageId}":{"delete":{"tags":["Messages"],"summary":"Delete a message","description":"Deletes a message together with its reactions and attachments, and tells the members of the room. Only the message's author, or the creator or admin of the room, may delete it; anyone else gets the same 400 as for a message that does not exist.","operationId":"deleteMessage","parameters":[{"name":"messageId","in":"path","description":"Id of the message","required":true,"schema":{"type":"string"}}],"responses":{"204":{"description":"The message was deleted"},"400":{"description":"Invalid request, or the referenced entity does not exist or is not accessible","content":{"application/json":{"schema":{"$ref":"#/components/schemas/ErrorResponse"}}}},"401":{"description":"The access token is missing, expired or invalid, or the caller is not allowed to do this","content":{"application/json":{"schema":{"$ref":"#/components/schemas/ErrorResponse"}}}},"500":{"description":"Unexpected server error","content":{"application/json":{"schema":{"$ref":"#/components/schemas/ErrorResponse"}}}}}}},"/api/comments/{commentId}":{"delete":{"tags":["Messages"],"summary":"Delete a comment","description":"Deletes one of the caller's own comments together with its reactions. Anyone else's comment is reported with the same 400 as one that does not exist.","operationId":"deleteComment","parameters":[{"name":"commentId","in":"path","description":"Id of the comment","required":true,"schema":{"type":"string"}}],"responses":{"204":{"description":"The comment was deleted"},"400":{"description":"Invalid request, or the referenced entity does not exist or is not accessible","content":{"application/json":{"schema":{"$ref":"#/components/schemas/ErrorResponse"}}}},"401":{"description":"The access token is missing, expired or invalid, or the caller is not allowed to do this","content":{"application/json":{"schema":{"$ref":"#/components/schemas/ErrorResponse"}}}},"500":{"description":"Unexpected server error","content":{"application/json":{"schema":{"$ref":"#/components/schemas/ErrorResponse"}}}}}}}},"components":{"schemas":{"ChatMessage":{"type":"object","description":"A message to post to a room.","properties":{"roomId":{"type":"string","description":"Id of the room to post to; must match the room in the path"},"message":{"type":"string","description":"The text of the message. In an end-to-end encrypted room it must already be encrypted"},"userId":{"type":"string","description":"Ignored: the author is always the authenticated caller"},"language":{"type":"string","description":"Language code of the message text","example":"en"},"deliverToUserId":{"type":["string","null"],"description":"When set, deliver the message to this member only"},"geoLocation":{"$ref":"#/components/schemas/GeoLocation","description":"Where the message was sent from; when absent it is derived from the IP address of the request"},"mentions":{"type":["object","null"],"additionalProperties":{"type":"string"},"description":"Mentions in the text: `@User Name` or `#Some Room` mapped to the id of that user or room"},"attachmentIds":{"type":["array","null"],"description":"Ids of the encrypted attachments uploaded for this message, for an end-to-end encrypted room","items":{"type":"string"}}}},"GeoLocation":{"type":"object","description":"A position on earth.","properties":{"latitude":{"type":"number","format":"double","description":"Latitude in decimal degrees"},"longitude":{"type":"number","format":"double","description":"Longitude in decimal degrees"}}},"PostTranslations":{"type":"object","description":"What one broadcast post says in each language it has, and where each text came from.","properties":{"sourceLanguage":{"type":"string","description":"Code of the language the post was written in","example":"en"},"texts":{"type":"object","additionalProperties":{"type":"string"},"description":"The post's text per language code, including the language it was written in"},"machineTranslatedLanguages":{"type":"array","description":"Codes of the languages whose text came from the translation provider rather than from a person, and so has not been checked","items":{"type":"string"},"uniqueItems":true}}},"E2eeIdentityRequest":{"type":"object","description":"The identity keys of the calling device.","properties":{"x25519PublicKey":{"type":"string","description":"Public key that room keys will be wrapped to"},"ed25519PublicKey":{"type":"string","description":"Public key that verifies the shares the device sends"},"proof":{"type":"string","description":"Proof that the device holds the private key of the Ed25519 key"}}},"E2eeKeyBackupRequest":{"type":"object","description":"A backup of the caller's room keys to store.","properties":{"backupVersion":{"type":"integer","format":"int64","description":"Version of the backup; must be higher than the stored one"},"kdf":{"type":"string","description":"Name of the key derivation function"},"kdfParams":{"type":"string","description":"Parameters of the key derivation function, opaque to the server"},"ciphertext":{"type":"string","description":"The encrypted backup, opaque to the server"}}},"VoipPushTokenUpdateRequest":{"type":"object","description":"Registers the PushKit VoIP token of the calling device.","properties":{"voipPushToken":{"type":"string","description":"The PushKit VoIP token","minLength":1}},"required":["voipPushToken"]},"PushTokenUpdateRequest":{"type":"object","description":"Registers the FCM push token of the calling device.","properties":{"pushToken":{"type":"string","description":"The FCM registration token","minLength":1}},"required":["pushToken"]},"BiometricKeyEnrollRequest":{"type":"object","description":"Enrolls the biometric signing key of the calling device.","properties":{"publicKey":{"type":"string","description":"Public half of the biometric-gated signing key","minLength":1},"keyAlgorithm":{"type":"string","description":"Algorithm of the key","minLength":1},"nonce":{"type":"string","description":"Single-use value that is signed to prove possession of the private key","minLength":1},"signature":{"type":"string","description":"Signature over the nonce made with the private key","minLength":1},"currentKeySignature":{"type":"string","description":"When a key is already enrolled: signature of BIOMETRIC_KEY_CHANGE_V1|REPLACE|<nonce> made with the current key. Not needed with a code"},"code":{"type":"string","description":"When a key is already enrolled and cannot sign: the code sent by POST /api/devices/biometric-key/code"}},"required":["keyAlgorithm","nonce","publicKey","signature"]},"WalletDto":{"type":"object","description":"A cryptocurrency wallet.","properties":{"id":{"type":"string","description":"Unique id of the wallet","example":"2bTBIYWDF7nl1mcJ2KRbOSIuM16"},"userId":{"type":"string","description":"Id of the user owning the wallet; absent for an app's wallet"},"appId":{"type":"string","description":"Id of the app owning the wallet; absent for a user's wallet"},"name":{"type":"string","description":"Name of the wallet, which the owner can change"},"walletType":{"type":"string","description":"The currency of the wallet","enum":["Bitcoin","Ethereum","USDBG"]},"createTime":{"type":"string","format":"date-time","description":"When the wallet was created"},"balance":{"type":"number","description":"Current balance, in the wallet's native unit"}}},"WithdrawalCreateRequest":{"type":"object","description":"A withdrawal to make from one of the caller's wallets to an external address.","properties":{"walletId":{"type":"string","description":"Id of the wallet to withdraw from; must belong to the caller"},"toAddress":{"type":"string","description":"Destination address, valid for the wallet's network"},"amount":{"type":"number","description":"Amount, in the wallet's native unit"},"requestId":{"type":"string","description":"Id the client gives this withdrawal, mandatory and unique per user. Repeating a request with the same id and the same wallet, address and amount returns the first result and moves nothing; with anything different it is refused with 409. Keep it across retries of one request; use a new one for a new request"},"issuedAt":{"type":["integer","null"],"format":"int64","description":"When the request was signed, in epoch milliseconds; required with `biometricSignature`. Refused when more than 5 minutes from the server's clock"},"biometricSignature":{"type":["string","null"],"description":"Base64 RSA-SHA256 signature of WALLET_WITHDRAWAL_V2|walletId|toAddress|amount (8 decimals)|requestId|issuedAt, made with the device's biometric-gated key; required only when the device has enrolled one"}}},"WithdrawalRequestDto":{"type":"object","description":"A withdrawal to an external address.","properties":{"id":{"type":"string","description":"Id of the withdrawal"},"walletId":{"type":"string","description":"Id of the wallet the funds came from"},"walletType":{"type":"string","description":"The currency of the wallet","enum":["Bitcoin","Ethereum","USDBG"]},"toAddress":{"type":"string","description":"Destination address"},"amount":{"type":"number","description":"Amount withdrawn"},"networkFee":{"type":"number","description":"Network fee charged"},"status":{"type":"string","description":"BROADCAST once sent to the network, CONFIRMED once it has enough confirmations, FAILED when it was mined but failed on chain (nothing was sent; the amount and fee were refunded to the wallet)","enum":["PENDING_BROADCAST","BROADCAST","CONFIRMED","FAILED"]},"broadcastTxid":{"type":"string","description":"Id of the blockchain transaction"},"createTime":{"type":"string","format":"date-time","description":"When the withdrawal was made"}}},"WalletTransactionRequest":{"type":"object","description":"A transfer to make from one of the caller's wallets.","properties":{"fromWalletId":{"type":"string","description":"Id of the wallet to take the funds from; must belong to the caller"},"toWalletId":{"type":"string","description":"Id of the wallet to send the funds to. Give either this or `toUserId`"},"toUserId":{"type":"string","description":"Id of the user to send the funds to. Give either this or `toWalletId`"},"amount":{"type":"number","description":"Amount, in the wallet's native unit"},"paymentRequestItemId":{"type":["string","null"],"description":"Id of the payment request item this transfer pays. It is then checked against the item (recipient, amount, target wallet) and marks it as paid"},"requestId":{"type":"string","description":"Id the client gives this transfer, mandatory and unique per user. Repeating a request with the same id and the same wallets, recipient and amount returns the first result and moves nothing; with anything different it is refused with 409. Keep it across retries of one request; use a new one for a new request"},"issuedAt":{"type":["integer","null"],"format":"int64","description":"When the request was signed, in epoch milliseconds; required with `biometricSignature`. Refused when more than 5 minutes from the server's clock"},"biometricSignature":{"type":["string","null"],"description":"Base64 RSA-SHA256 signature of WALLET_TRANSACTION_V2|fromWalletId|toWalletId|toUserId|amount (8 decimals)|paymentRequestItemId|requestId|issuedAt (empty for absent values), made with the device's biometric-gated key; required only when the device has enrolled one"}}},"AppPaymentRef":{"type":"object","description":"The app payment a transaction settles.","properties":{"id":{"type":"string","description":"Id of the payment"},"appId":{"type":"string","description":"Id of the app"},"appName":{"type":"string","description":"The app's registered name"},"kind":{"type":"string","description":"CHARGE, REFUND or PAYOUT"},"reference":{"type":"string","description":"The app's own reference"},"description":{"type":"string","description":"The app's description of what the payment is for"},"receipt":{"type":"object","additionalProperties":{},"description":"On a charge the caller paid: the app's receipt card, once the app answered with one; shown privately, never posted"},"roomId":{"type":"string","description":"The room the payment was asked in, when it was asked in one: the receipt's buttons run there; never sent to the app"}}},"UserMinimalDto":{"type":"object","description":"A user's id, name and phone number, for lists.","properties":{"id":{"type":"string","description":"Unique id of the user","example":"2bTBIYWDF7nl1mcJ2KRbOSIuM16"},"name":{"type":"string","description":"Display name"},"phone":{"type":"string","description":"Phone number in international format"}}},"WalletTransactionDto":{"type":"object","description":"A transaction of a wallet.","properties":{"id":{"type":"string","description":"Id of the transaction"},"createTime":{"type":"string","format":"date-time","description":"When the transaction was made"},"fromWalletId":{"type":["string","null"],"description":"Id of the wallet the funds left"},"toWalletId":{"type":["string","null"],"description":"Id of the wallet the funds went to"},"fromUser":{"$ref":"#/components/schemas/UserMinimalDto","description":"The user who sent the funds"},"toUser":{"$ref":"#/components/schemas/UserMinimalDto","description":"The user who received the funds"},"amount":{"type":"number","description":"Amount, in the wallet's native unit"},"networkFee":{"type":["number","null"],"description":"Network fee, when the transaction went out on the blockchain"},"transactionType":{"type":"string","description":"INTERNAL for a transfer between wallets of this service, EXTERNAL for one on the blockchain, SWAP for one leg of a conversion between two of the user's own wallets","enum":["INTERNAL","EXTERNAL","SWAP","APP_CHARGE","APP_FEE","APP_REFUND","APP_PAYOUT","WITHDRAWAL_REFUND"]},"externalTransactionId":{"type":["string","null"],"description":"Id of the blockchain transaction, for an EXTERNAL one"},"swapId":{"type":["string","null"],"description":"Id of the conversion this transaction is one leg of, for a SWAP one"},"chainStatus":{"type":["string","null"],"description":"State on the blockchain, for an EXTERNAL one: BROADCAST while a withdrawal waits for the network, CONFIRMED once it has enough blocks, and always CONFIRMED for a deposit. Left out when it is not known","enum":["PENDING_BROADCAST","BROADCAST","CONFIRMED","FAILED"]},"toAddress":{"type":["string","null"],"description":"Address the funds were sent to, for an outgoing EXTERNAL one"},"withdrawalId":{"type":["string","null"],"description":"Id of the withdrawal, for an outgoing EXTERNAL one, to refresh it with GET /api/wallet-withdrawals/{id}"},"appPayment":{"$ref":"#/components/schemas/AppPaymentRef","description":"For an APP_CHARGE, APP_REFUND or APP_PAYOUT row (proposal #82): the app and the payment it settles, so the history can name the app and what the payment was for"}}},"WalletSwapExecuteRequest":{"type":"object","description":"The quote to accept.","properties":{"quoteId":{"type":"string","description":"Id of the quote, from the quote response"},"nonce":{"type":["string","null"],"description":"Single-use value that was signed; required when `biometricSignature` is sent"},"biometricSignature":{"type":["string","null"],"description":"Base64 signature of `WALLET_SWAP_V1|quoteId|fromWalletId|toWalletId|amount|nonce` made with the device's biometric key, the amount with 8 decimal places; required when the device has a biometric key"}}},"WalletSwapDto":{"type":"object","description":"A conversion between two of the user's wallets.","properties":{"id":{"type":"string","description":"Id of the conversion; the same as the id of the quote that was accepted"},"createTime":{"type":"string","format":"date-time","description":"When the quote was accepted"},"fromWalletId":{"type":"string","description":"Id of the wallet converted from"},"toWalletId":{"type":"string","description":"Id of the wallet converted into"},"fromWalletType":{"type":"string","description":"Currency converted from","enum":["Bitcoin","Ethereum","USDBG"]},"toWalletType":{"type":"string","description":"Currency converted into","enum":["Bitcoin","Ethereum","USDBG"]},"fromAmount":{"type":"number","description":"Amount debited from the wallet converted from"},"toAmount":{"type":"number","description":"Amount credited to the wallet converted into, after the fee"},"rate":{"type":"number","description":"Units of the currency converted into, per one unit of the currency converted from, before the fee"},"feeAmount":{"type":"number","description":"The whole fee, in the currency converted into"},"status":{"type":"string","description":"PENDING while the trade is not filled yet, EXECUTED once both wallets are updated, FAILED when the trade did not happen and nothing was converted","enum":["PENDING","EXECUTED","FAILED"]},"executedAt":{"type":["string","null"],"format":"date-time","description":"When both wallets were updated"}}},"WalletSwapQuoteRequest":{"type":"object","description":"What to convert.","properties":{"fromWalletId":{"type":"string","description":"Id of the wallet to convert from"},"toWalletId":{"type":"string","description":"Id of the wallet to convert into; a wallet of the same user and of another currency"},"amount":{"type":"number","description":"Amount to convert, in the unit of the wallet converted from"}}},"WalletSwapQuoteDto":{"type":"object","description":"A price for a conversion, which can be accepted until it expires.","properties":{"quoteId":{"type":"string","description":"Id of the quote, to send back when accepting it"},"fromWalletId":{"type":"string","description":"Id of the wallet converted from"},"toWalletId":{"type":"string","description":"Id of the wallet converted into"},"fromWalletType":{"type":"string","description":"Currency converted from","enum":["Bitcoin","Ethereum","USDBG"]},"toWalletType":{"type":"string","description":"Currency converted into","enum":["Bitcoin","Ethereum","USDBG"]},"fromAmount":{"type":"number","description":"Amount the wallet converted from would be debited"},"toAmount":{"type":"number","description":"Amount the wallet converted into would be credited, after the fee"},"rate":{"type":"number","description":"Units of the currency converted into, per one unit of the currency converted from, before the fee"},"feeAmount":{"type":"number","description":"The whole fee, in the currency converted into; already taken out of `toAmount`"},"feeBps":{"type":"integer","format":"int32","description":"The fee in basis points of the converted amount; 100 is 1%"},"sufficientBalance":{"type":"boolean","description":"Whether the wallet converted from holds `fromAmount` at this moment"},"expiresAt":{"type":"string","format":"date-time","description":"When the quote stops being acceptable"},"expiresInMillis":{"type":"integer","format":"int64","description":"Milliseconds the quote can still be accepted for, counted from when it was given; for a countdown that does not depend on the clock of the device"}}},"WalletLimitRequestCreateRequest":{"type":"object","description":"A request for higher sending limits, signed with the device's biometric key.","properties":{"tier":{"type":"string","description":"The tier asked for, by name, from GET /api/wallet-limits/request"},"duration":{"type":"string","description":"For how long: DAYS_7, DAYS_30 or UNTIL_CHANGED","enum":["DAYS_7","DAYS_30","UNTIL_CHANGED"]},"reason":{"type":["string","null"],"description":"What it is for, up to 500 characters; optional"},"requestId":{"type":"string","description":"A new id for this request, up to 64 characters; the single-use value of the signature"},"issuedAt":{"type":"integer","format":"int64","description":"When the request was signed, in epoch milliseconds; refused when more than 5 minutes from the server's clock"},"biometricSignature":{"type":"string","description":"Base64 RSA-SHA256 signature over WALLET_LIMIT_REQUEST_V1|tier|duration|requestId|issuedAt, made with the device's biometric key; required"}}},"WalletLimitRequestDto":{"type":"object","description":"A request for higher sending limits and what became of it.","properties":{"id":{"type":"string","description":"The request's id"},"tier":{"type":"string","description":"The tier asked for: plus, pro, business or no-limit"},"perTransactionUsd":{"type":"number","description":"The per-transaction limit asked for, in US dollars; absent for no-limit"},"dailyUsd":{"type":"number","description":"The limit for the window asked for, in US dollars; absent for no-limit"},"unlimited":{"type":"boolean","description":"Whether no limit at all was asked for"},"duration":{"type":"string","description":"For how long: DAYS_7, DAYS_30 or UNTIL_CHANGED","enum":["DAYS_7","DAYS_30","UNTIL_CHANGED"]},"reason":{"type":"string","description":"What it is for, in the member's words; absent when not given"},"status":{"type":"string","description":"OPEN, APPROVED, REFUSED or CANCELLED","enum":["OPEN","APPROVED","REFUSED","CANCELLED"]},"createTime":{"type":"string","format":"date-time","description":"When it was asked"},"decidedTime":{"type":"string","format":"date-time","description":"When it was approved, refused or cancelled; absent while open"},"decisionNote":{"type":"string","description":"Why it was refused, from support; absent otherwise"}}},"UserDto":{"type":"object","description":"A user's public profile and account state.","properties":{"id":{"type":"string","description":"Unique id of the user","example":"2bTBIYWDF7nl1mcJ2KRbOSIuM16"},"createTime":{"type":"string","format":"date-time","description":"When the account was created"},"email":{"type":"string","description":"Email address, when the user has given one"},"name":{"type":"string","description":"Display name"},"phone":{"type":"string","description":"Phone number in international format, which the user signs in with","example":"+15551234567"},"enabled":{"type":"boolean","description":"Whether the account is enabled"},"accountActivated":{"type":"boolean","description":"Whether the account has been activated"},"using2Fa":{"type":"boolean","description":"Whether two-factor authentication is on"},"lastUpdated":{"type":"string","format":"date-time","description":"When the account was last changed"},"userStatus":{"type":"string","description":"Presence of the user: OFFLINE, ONLINE, AWAY or BUSY (do not disturb)","enum":["OFFLINE","ONLINE","AWAY","BUSY"]}}},"UserBlockDto":{"type":"object","description":"A block, seen from the blocker's side.","properties":{"id":{"type":"string","description":"Id of the block; use it to remove the block"},"blockerUserId":{"type":"string","description":"Id of the user who blocked"},"blockedUserId":{"type":"string","description":"Id of the user who is blocked"},"blockedUserName":{"type":"string","description":"Display name of the blocked user"},"reason":{"type":"string","description":"Why the user was blocked","enum":["UNKNOWN","SPAM","HARASSMENT","INAPPROPRIATE_CONTENT","DONT_KNOW_PERSON","OTHER"]},"createTime":{"type":"string","format":"date-time","description":"When the block was made"}}},"User":{"type":"object","description":"A user account, as returned by the two-factor endpoints.","properties":{"id":{"type":"string","description":"Unique id of the user","example":"2bTBIYWDF7nl1mcJ2KRbOSIuM16"},"createTime":{"type":"string","format":"date-time","description":"When the account was created"},"email":{"type":"string","description":"Email address, when the user has given one"},"phone":{"type":"string","description":"Phone number in international format, which the user signs in with","example":"+15551234567"},"name":{"type":"string","description":"Display name"},"enabled":{"type":"boolean","description":"Whether the account is enabled"},"accountActivated":{"type":"boolean","description":"Whether the account has been activated"},"using2Fa":{"type":"boolean","description":"Whether two-factor authentication is on"},"lastUpdated":{"type":"string","format":"date-time","description":"When the account was last changed"},"userStatus":{"type":"string","description":"Presence of the user: OFFLINE, ONLINE, AWAY or BUSY (do not disturb)","enum":["OFFLINE","ONLINE","AWAY","BUSY"]},"registrationComplete":{"type":"boolean","description":"Whether the user has finished registering by giving a name"}}},"AudioUploadForm":{"type":"object","description":"A recorded voice message to transcribe.","properties":{"file":{"type":"string","format":"binary","description":"The audio recording"}}},"SpeachToTextResponse":{"type":"object","description":"The outcome of transcribing an audio recording.","properties":{"text":{"type":"string","description":"The transcript; absent on failure"},"error":{"type":"string","description":"What went wrong; absent on success"}}},"ErrorResponse":{"description":"The body of every error answer.","properties":{"timestamp":{"type":"string","format":"date-time","description":"When the error happened, in UTC"},"status":{"type":"integer","format":"int32","description":"HTTP status code"},"error":{"type":"string","description":"HTTP reason phrase, such as Bad Request"},"exception":{"type":"string","description":"Simple class name of the exception behind the error"},"requestId":{"type":"string","description":"Id of the request, also sent as the `X-Request-ID` response header, for finding it in the logs"},"message":{"type":"string","description":"What went wrong. Generic for server errors"}}},"RoomDto":{"type":"object","description":"A chat room.","properties":{"id":{"type":"string","description":"Unique id of the room","example":"2bTBIYWDF7nl1mcJ2KRbOSIuM16"},"name":{"type":"string","description":"Name of the room"},"description":{"type":"string","description":"Description of the room"},"creatorUserId":{"type":"string","description":"Id of the user who created the room"},"adminUserId":{"type":"string","description":"Id of the user who administers the room"},"roomType":{"type":"string","description":"Kind of room, such as a direct chat, a private or public group, or a broadcast room","enum":["PRIVATE_DIRECT","PRIVATE_GROUP","PUBLIC_GROUP","PUBLIC_BROADCAST"]},"createTime":{"type":"string","format":"date-time","description":"When the room was created"},"directIdPair":{"type":"string","description":"For a direct chat, the ids of its two users separated by a colon"},"userOneAvatarUrl":{"type":"string","description":"For a direct chat, avatar URL of the first user"},"userTwoAvatarUrl":{"type":"string","description":"For a direct chat, avatar URL of the second user"},"roomAvatarUrl":{"type":"string","description":"Avatar URL of the room"},"userCount":{"type":"integer","format":"int32","description":"Number of members"},"postCount":{"type":"integer","format":"int32","description":"Number of messages posted in the room"},"participantPublishEnabled":{"type":"boolean","description":"Whether members other than the creator and admin may post"},"commentsEnabled":{"type":"boolean","description":"Whether members may comment on messages"},"e2ee":{"type":"boolean","description":"Whether the room is end-to-end encrypted"},"e2eeKeyVersion":{"type":"integer","format":"int32","description":"Current version of the room key; below 1 until the first key exists"}}},"RoomSearchRequest":{"type":"object","description":"Searches public rooms.","properties":{"searchQuery":{"type":"string","description":"Text to look for in room names; used with QUERY_SEARCH"},"roomSearchType":{"type":"string","description":"QUERY_SEARCH to match `searchQuery`, MOST_POPULAR_SEARCH for the largest rooms","enum":["QUERY_SEARCH","MOST_POPULAR_SEARCH"]},"pageSize":{"type":"integer","format":"int32","description":"Rooms per page"},"currentPage":{"type":"integer","format":"int32","description":"Zero-based page number"}}},"PageMetadata":{"type":"object","properties":{"size":{"type":"integer","format":"int64"},"number":{"type":"integer","format":"int64"},"totalElements":{"type":"integer","format":"int64"},"totalPages":{"type":"integer","format":"int64"}}},"PagedModelRoomDto":{"type":"object","properties":{"content":{"type":"array","items":{"$ref":"#/components/schemas/RoomDto"}},"page":{"$ref":"#/components/schemas/PageMetadata"}}},"PaymentRequestCreateRequest":{"type":"object","description":"A request for payment to make.","properties":{"targetWalletId":{"type":"string","description":"Id of the caller's wallet the payments go to"},"recipientAmounts":{"type":"object","additionalProperties":{"type":"number"},"description":"The amount each person is asked to pay, keyed by the id of that user"},"notes":{"type":["string","null"],"description":"Note shown with the request"}}},"PaymentRequestDto":{"type":"object","description":"A request for payment, with one item per person asked.","properties":{"id":{"type":"string","description":"Id of the payment request"},"createTime":{"type":"string","format":"date-time","description":"When the request was created"},"targetWalletId":{"type":"string","description":"Id of the wallet the payments go to"},"creatorUserId":{"type":"string","description":"Id of the user who asked for payment"},"walletType":{"type":"string","description":"The currency of the target wallet","enum":["Bitcoin","Ethereum","USDBG"]},"totalAmount":{"type":"number","description":"Total amount asked for, over all items"},"totalAmountPaid":{"type":"number","description":"Total amount paid so far"},"notes":{"type":"string","description":"Note shown with the request"},"metadata":{"type":"object","additionalProperties":{},"description":"Additional data attached to the request"},"type":{"type":"string","description":"Payment state: Paid, PartiallyPaid, Unpaid or Cancelled","enum":["Paid","PartiallyPaid","Unpaid","Cancelled"]},"items":{"type":"array","description":"What each person is asked to pay","items":{"$ref":"#/components/schemas/PaymentRequestItemDto"}}}},"PaymentRequestItemDto":{"type":"object","description":"What one person is asked to pay in a payment request.","properties":{"id":{"type":"string","description":"Id of the item"},"createTime":{"type":"string","format":"date-time","description":"When the item was created"},"paymentRequestId":{"type":"string","description":"Id of the payment request the item belongs to"},"recipientUserId":{"type":"string","description":"Id of the user asked to pay"},"recipientUserName":{"type":"string","description":"Display name of the user asked to pay"},"amount":{"type":"number","description":"Amount asked"},"amountPaid":{"type":"number","description":"Amount paid so far"},"walletTransactionId":{"type":"string","description":"Id of the transaction that paid the item, once it is paid"}}},"AttachmentsUploadForm":{"type":"object","description":"Files to attach to a room, optionally with the message they belong to.","properties":{"files":{"type":"array","description":"The files to upload","items":{"type":"string","format":"binary"}},"payload":{"type":"string","description":"Optional JSON `ChatMessage` to post together with the files"}}},"TldrRequestedDto":{"type":"object","description":"Answer to a request for a summary; the summary itself follows later.","properties":{"requestId":{"type":"string","description":"Id of the request, to match the `chat.tldr-ready` event that carries the summary"}}},"MediaTicketDto":{"type":"object","description":"A short-lived link to one attachment that needs no `Authorization` header.","properties":{"url":{"type":"string","description":"Path of the attachment with its ticket. `download=true` or `thumbnail=true` may be added"},"expiresAt":{"type":"string","format":"date-time","description":"When the link stops working"}}},"E2eeKeyShareUploadRequest":{"type":"object","description":"Wrapped copies of a room key to store.","properties":{"keyVersion":{"type":"integer","format":"int32","description":"Version of the room key the shares are for"},"create":{"type":"boolean","description":"true to create this version, as the first key or a rotation, which needs a share for every eligible device; false to fill an existing version in for devices that lack it"},"shares":{"type":"array","description":"The wrapped copies, one per device","items":{"$ref":"#/components/schemas/Share"}}}},"Share":{"type":"object","description":"The room key wrapped for one recipient device.","properties":{"deviceId":{"type":"string","description":"Id of the device the key is wrapped for"},"wrappedKey":{"type":"string","description":"The room key, wrapped for the device"},"wrapAlgorithm":{"type":"string","description":"Algorithm used to wrap the key"},"senderSignature":{"type":"string","description":"Signature of the uploading device over the share"}}},"E2eeKeyRequest":{"type":"object","description":"A request for a room key version the device lacks.","properties":{"keyVersion":{"type":"integer","format":"int32","description":"The missing key version, from 1 to the room's current version"}}},"EncryptedAttachmentUploadForm":{"type":"object","description":"An end-to-end encrypted attachment, with its thumbnail when it has one.","properties":{"blob":{"type":"string","format":"binary","description":"The encrypted attachment"},"thumbnail":{"type":"string","format":"binary","description":"The encrypted thumbnail, if the attachment has one"}}},"E2eeAttachmentUploadedDto":{"type":"object","description":"The outcome of storing an encrypted attachment.","properties":{"attachmentId":{"type":"string","description":"Id to put in the message's `attachmentIds`"},"size":{"type":"integer","format":"int64","description":"Number of bytes stored"}}},"OtpChallengeCreated":{"type":"object","description":"A code was sent by SMS.","properties":{"id":{"type":"string","description":"Id of the challenge the code belongs to"},"expiresAt":{"type":"string","format":"date-time","description":"When the code stops working"}}},"MessageCommentDto":{"type":"object","description":"A comment on a message.","properties":{"id":{"type":"string","description":"Id of the comment"},"createTime":{"type":"string","format":"date-time","description":"When the comment was posted"},"roomId":{"type":"string","description":"Id of the room"},"messageId":{"type":"string","description":"Id of the message commented on"},"creatorUserId":{"type":"string","description":"Id of the author"},"creatorUserName":{"type":"string","description":"Display name of the author"},"text":{"type":"string","description":"The text of the comment"},"emoji":{"type":"object","additionalProperties":{"type":"array","items":{"$ref":"#/components/schemas/UserShortDto"}},"description":"Reactions on the comment: each emoji with the users who chose it"},"emojiCount":{"type":"integer","format":"int32","description":"Number of reactions on the comment"}}},"UserShortDto":{"type":"object","description":"A user's id and name, used to show who reacted to a message or comment.","properties":{"id":{"type":"string","description":"Unique id of the user","example":"2bTBIYWDF7nl1mcJ2KRbOSIuM16"},"name":{"type":"string","description":"Display name"}}},"CallCreateRequest":{"type":"object","description":"A call to start.","properties":{"roomId":{"type":"string","description":"Id of the room the call is placed in"},"calleeUserId":{"type":"string","description":"Id of the user to call"},"audio":{"type":"boolean","description":"Whether the call carries audio"},"video":{"type":"boolean","description":"Whether the call carries video"}}},"CallResponse":{"type":"object","description":"The state of a call.","properties":{"callId":{"type":"string","description":"Id of the call"},"roomId":{"type":"string","description":"Id of the room the call was placed in"},"callerUserId":{"type":"string","description":"Id of the user who started the call"},"calleeUserId":{"type":"string","description":"Id of the user who was called"},"state":{"type":"string","description":"Where the call is in its life: NEW, RINGING, ACCEPTED, NEGOTIATING, CONNECTED, or an end state DECLINED, MISSED, FAILED or ENDED","enum":["NEW","RINGING","ACCEPTED","NEGOTIATING","CONNECTED","DECLINED","MISSED","FAILED","ENDED"]},"audioEnabled":{"type":"boolean","description":"Whether the call carries audio"},"videoEnabled":{"type":"boolean","description":"Whether the call carries video"},"createTime":{"type":"string","format":"date-time","description":"When the call was started"},"acceptedAt":{"type":"string","format":"date-time","description":"When the callee answered; absent until then"},"connectedAt":{"type":"string","format":"date-time","description":"When the media connection was established; absent until then"},"endedAt":{"type":"string","format":"date-time","description":"When the call ended; absent while it is going on"},"endReason":{"type":"string","description":"Why the call ended; absent while it is going on","enum":["USER_HANGUP","DECLINED","BUSY","MISSED","ICE_FAILURE","SIGNALING_FAILURE","MEDIA_PERMISSION_DENIED","PEER_DISCONNECTED","SERVER_TIMEOUT"]}}},"ImageUploadForm":{"type":"object","description":"An image to use as an avatar.","properties":{"file":{"type":"string","format":"binary","description":"The image file"}}},"RefreshTokenRequest":{"type":"object","description":"Exchanges a refresh token for a new token pair.","properties":{"refreshToken":{"type":"string","description":"The refresh token from the latest token pair","minLength":1},"deviceId":{"type":"string","description":"Id of the device the token pair was issued to","minLength":1}},"required":["deviceId","refreshToken"]},"OAuth2TokenPair":{"type":"object","description":"The access and refresh token issued after signing in or refreshing.","properties":{"accessToken":{"type":"string","description":"JWT to send as `Authorization: Bearer <token>`"},"accessTokenExpiresAt":{"type":"string","format":"date-time","description":"When the access token expires"},"refreshToken":{"type":"string","description":"Token to exchange for a new pair with `POST /api/auth/token/refresh`"},"refreshTokenExpiresAt":{"type":"string","format":"date-time","description":"When the refresh token expires"},"refreshSessionId":{"type":"string","description":"Id of the refresh session the tokens belong to"},"twoFactorAuthenticationRequired":{"type":"boolean","description":"Whether the user still has to complete two-factor authentication. When true there is no access or refresh token yet, only the pending token"},"pendingToken":{"type":"string","description":"For an account with two-factor authentication after its SMS code: the token to send with the authenticator code to `POST /api/auth/2fa/verify`, which then returns the token pair. It opens nothing else"},"pendingTokenExpiresAt":{"type":"string","format":"date-time","description":"When the pending token expires"},"accountCreated":{"type":"boolean","description":"Whether this sign-in created the account, so the app continues with registration"}}},"LogoutRequest":{"type":"object","description":"Ends the refresh session of a refresh token.","properties":{"refreshToken":{"type":"string","description":"The refresh token from the latest token pair","minLength":1}},"required":["refreshToken"]},"KeyPairPem":{"type":"object","description":"An RSA key pair in PEM format.","properties":{"publicKeyPem":{"type":"string","description":"The public key, PEM encoded"},"privateKeyPem":{"type":"string","description":"The private key, PEM encoded"}}},"AppSessionTokenDto":{"type":"object","description":"A session token for a web app's backend, verifiable with the JWKS at /api/apps/signing-keys.","properties":{"token":{"type":"string","description":"Compact JWS (EdDSA over Ed25519): iss banga-apps, aud the app id, sub the per-app user id, scope, jti, iat, exp"},"expiresAt":{"type":"string","format":"date-time","description":"When the token stops being valid"},"appUserId":{"type":"string","description":"The per-app user id the token names, the same the webhook sees"}}},"AppReportRequest":{"type":"object","description":"Why a user is reporting an app.","properties":{"reason":{"type":"string","description":"Free text, at most 500 characters","example":"Asks for my password"}}},"AppCardDto":{"type":"object","description":"A card posted into a room by a user on behalf of an app; data the client draws natively.","properties":{"app":{"$ref":"#/components/schemas/AppRef","description":"The app the card came from"},"blocks":{"type":"array","description":"Blocks in display order: title, text, facts, image, status, divider","items":{"type":"object","additionalProperties":{}}},"actions":{"type":"array","description":"Buttons under the card: open_flow, submit, open_url, open_app","items":{"type":"object","additionalProperties":{}}},"sig":{"$ref":"#/components/schemas/Signature","description":"chat-core's signature over the card, for the receiving device to verify"}}},"AppPaymentCreateRequest":{"type":"object","description":"What a device sends to start a payment to an app.","properties":{"card":{"$ref":"#/components/schemas/AppCardDto","description":"From a card: the signed card that carries the pay action, exactly as received"},"actionIndex":{"type":["integer","null"],"format":"int32","description":"From a card: index of the pay action in the card's actions"},"amount":{"type":["number","null"],"description":"From a web app: the amount the page asks for"},"currency":{"type":["string","null"],"description":"From a web app: the currency","enum":["Bitcoin","Ethereum","USDBG"]},"reference":{"type":["string","null"],"description":"From a web app: the app's reference, unique per user"},"description":{"type":["string","null"],"description":"From a web app: what the payment is for, shown on the sheet"},"roomId":{"type":["string","null"],"description":"The room the ask happened in, if any; used only to show the room on the device"}}},"AppRef":{"type":"object","description":"The app a card came from.","properties":{"id":{"type":"string","description":"Id of the app"},"name":{"type":"string","description":"Name of the app at the time; the client shows the registry's current name when it has it"},"manifestHash":{"type":"string","description":"SHA-256 of the manifest version the card was produced under"}}},"Signature":{"type":"object","description":"Ed25519 signature by chat-core over the canonical form of the card.","properties":{"kid":{"type":"string","description":"Id of the signing key in the JWKS at /api/apps/signing-keys","example":"apps-1"},"alg":{"type":"string","description":"Signature algorithm; always Ed25519","example":"Ed25519"},"ts":{"type":"integer","format":"int64","description":"Unix seconds when the card was signed"},"userId":{"type":"string","description":"Id of the user the card was issued to, who is the only one who may post it"},"value":{"type":"string","description":"Base64url of the signature bytes"}}},"AppPaymentDto":{"type":"object","description":"A payment an app asked a user for, or sent a user.","properties":{"id":{"type":"string","description":"Id of the payment","example":"2bTBIYWDF7nl1mcJ2KRbOSIuM16"},"appId":{"type":"string","description":"Id of the app"},"appName":{"type":"string","description":"The app's registered name, to show on the sheet and in the history"},"kind":{"type":"string","description":"CHARGE (the user pays the app), REFUND or PAYOUT (the app pays the user)","enum":["CHARGE","REFUND","PAYOUT"]},"status":{"type":"string","description":"PENDING, COMPLETED, DECLINED, EXPIRED or FAILED","enum":["PENDING","COMPLETED","DECLINED","EXPIRED","FAILED"]},"currency":{"type":"string","description":"The currency","enum":["Bitcoin","Ethereum","USDBG"]},"amount":{"type":"number","description":"What the user pays or receives, in the currency's unit"},"reference":{"type":"string","description":"The app's own reference, such as an order number"},"description":{"type":"string","description":"The app's description of what the payment is for"},"createTime":{"type":"string","format":"date-time","description":"When the payment was asked for"},"expiresAt":{"type":["string","null"],"format":"date-time","description":"For a pending charge: when it lapses if not answered"},"completedAt":{"type":["string","null"],"format":"date-time","description":"When the money moved"},"roomId":{"type":["string","null"],"description":"The room the ask happened in, when it happened in one; never sent to the app"},"payerBalance":{"type":["number","null"],"description":"For a pending charge shown to the payer: the payer's balance in the currency"},"payerBalanceAfter":{"type":["number","null"],"description":"For a pending charge shown to the payer: the balance after paying"},"errorCode":{"type":["string","null"],"description":"For a failed charge: why, e.g. insufficient_balance"},"replyCard":{"type":["object","null"],"additionalProperties":{},"description":"For a completed charge: the app's receipt, the signed card it answered with (an AppCardDto), once it did; shown privately to the payer, never posted"}}},"AppPaymentConfirmRequest":{"type":"object","description":"The device's confirmation of a payment.","properties":{"nonce":{"type":"string","description":"A nonce the device made up, used once"},"biometricSignature":{"type":["string","null"],"description":"Base64 signature over APP_PAYMENT_V1|paymentId|appId|amount|currency|nonce with the device's biometric key; required when the device enrolled one"}}},"AppInvocationReplyDto":{"type":"object","description":"The outcome of an invocation.","properties":{"invocationId":{"type":"string","description":"Id of the invocation; sent back on the later steps of a dynamic flow"},"type":{"type":"string","description":"card, view, ack, error or payment"},"visibility":{"type":["string","null"],"description":"For a card: room (offer to post it into the room) or private (show it to this device only)"},"card":{"$ref":"#/components/schemas/AppCardDto","description":"For a card: the signed card, ready to post into the room as the message's card"},"view":{"type":"null","description":"For a view: the next view of the flow to show, as in a manifest"},"message":{"type":["string","null"],"description":"For an ack: a short message to show as a toast"},"userMessage":{"type":["string","null"],"description":"For an error: the app's message to the user"},"code":{"type":["string","null"],"description":"For an error: the app's own error code"},"payment":{"$ref":"#/components/schemas/AppPaymentDto","description":"For a payment: the pending charge to confirm or decline on the native sheet; a card, when also given, is shown after the sheet is answered"}}},"AppPaymentConfirmReplyDto":{"type":"object","description":"The outcome of confirming a payment.","properties":{"payment":{"$ref":"#/components/schemas/AppPaymentDto","description":"The payment, now completed"},"reply":{"$ref":"#/components/schemas/AppInvocationReplyDto","description":"The app's reply to the completed payment, if it answered in time: a private card, an ack or an error"}}},"Action":{"type":"object","description":"A card action being run.","properties":{"id":{"type":"string","description":"The action's id","example":"track"},"payload":{"type":["object","null"],"additionalProperties":{},"description":"The action's payload as it was in the card"}}},"AppInvocationRequest":{"type":"object","description":"A command or action invocation of an app.","properties":{"command":{"type":["string","null"],"description":"Id of the command being run, from the manifest; absent for a card action","example":"lunch.order"},"flow":{"type":["string","null"],"description":"Id of the flow being submitted after an open_flow action; absent when a command is being run","example":"order"},"step":{"type":["string","null"],"description":"For a dynamic flow, the id of the view the values were collected on; absent for a submit flow"},"invocationId":{"type":["string","null"],"description":"For the second and later steps of a dynamic flow, the invocation id the first step returned"},"values":{"type":["object","null"],"additionalProperties":{},"description":"The values collected, by input id"},"sealedValues":{"type":["string","null"],"description":"Instead of values, for an app with a sealing key: the values sealed on the device to the app's public key, as the contract's sealed envelope; the server forwards it without reading it"},"payload":{"type":["object","null"],"additionalProperties":{},"description":"With flow: the payload of the open_flow action that started it, as it was in the card; sent on every step and forwarded to the app next to values, unchecked apart from its size"},"action":{"$ref":"#/components/schemas/Action","description":"For a card action of type submit: the action"},"conversation":{"$ref":"#/components/schemas/Conversation","description":"Where the user is"},"location":{"$ref":"#/components/schemas/Location","description":"The user's location, only when they chose to share it for this invocation; forwarded rounded to two decimals and only under the location.coarse scope"}}},"Conversation":{"type":"object","description":"The room the invocation happens in; the app receives only an opaque reference to it.","properties":{"roomId":{"type":"string","description":"Id of the room; the caller must be a member"}}},"Location":{"type":"object","description":"A location the user chose to share for one invocation.","properties":{"lat":{"type":"number","format":"double","description":"Latitude in degrees"},"lon":{"type":"number","format":"double","description":"Longitude in degrees"}}},"AppInstallRequest":{"type":"object","description":"The scopes a user grants an app when installing it.","properties":{"scopes":{"type":"array","description":"Scopes granted, by manifest name; must be among the scopes the manifest asks for","example":["profile.name","post.cards"],"items":{"type":"string"}}}},"AppDto":{"type":"object","description":"An app of the bot/app platform.","properties":{"id":{"type":"string","description":"Id of the app","example":"2bTBIYWDF7nl1mcJ2KRbOSIuM16"},"slug":{"type":"string","description":"URL-safe name used in links and deep links","example":"lunch-orders"},"name":{"type":"string","description":"Display name","example":"Lunch Orders"},"description":{"type":"string","description":"Short description shown in the directory and on the install sheet"},"cardBot":{"type":"boolean","description":"Whether the app declares commands and a webhook"},"webApp":{"type":"boolean","description":"Whether the app declares a web page to open in the container"},"visibility":{"type":"string","description":"PRIVATE (owner and testers), UNLISTED (anyone with the link) or PUBLIC (in the directory)","enum":["PRIVATE","UNLISTED","PUBLIC"]},"preinstalled":{"type":"boolean","description":"Whether every account has the app without installing it"},"health":{"type":"string","description":"Reachability of the app's webhook: HEALTHY, DEGRADED, DOWN or UNKNOWN"}}},"InstalledAppDto":{"type":"object","description":"An installed app together with the manifest version the device should use.","properties":{"app":{"$ref":"#/components/schemas/AppDto","description":"The app"},"version":{"type":"integer","format":"int32","description":"Number of the manifest version below"},"manifestHash":{"type":"string","description":"SHA-256 of the manifest, as signed into cards"},"manifest":{"description":"The manifest: commands, flows, views, scopes and card templates, as the developer wrote it"},"grantedScopes":{"type":"array","description":"Scopes the user granted at install, by manifest name","example":["profile.name"],"items":{"type":"string"}},"consentNeeded":{"type":"boolean","description":"Whether the manifest asks for scopes the user has not granted yet, so the app should ask again"},"installedAt":{"type":"string","format":"date-time","description":"When the user installed the app; null for a preinstalled app"}}},"AppCardSignRequest":{"type":"object","description":"A card to sign for posting, as built by a web app's page.","properties":{"blocks":{"type":"array","description":"Blocks in display order: title, text, facts, image, status, divider","items":{"type":"object","additionalProperties":{}}},"actions":{"type":"array","description":"Buttons under the card: open_flow, submit, open_url, open_app; may be empty","items":{"type":"object","additionalProperties":{}}}}},"Contact":{"type":"object","description":"A person to send a connection request to, by email address and/or phone number.","properties":{"name":{"type":"string","description":"Name of the person"},"email":{"type":"string","description":"Email address of the person"},"phone":{"type":"string","description":"Phone number of the person, in international format"}}},"UserSettingsUpdateRequest":{"type":"object","description":"Changes to a user's profile and settings. Only the fields present are changed.","properties":{"name":{"type":"string","description":"New display name. Setting it completes the registration of a new user"},"email":{"type":"string","description":"New email address; must be valid and not used by another user"},"currency":{"type":"string","description":"Currency code to show wallet balances in; ignored when the exchange-rate table does not know it"},"language":{"type":"string","description":"Language code; must be a supported language","example":"en"},"theme":{"type":"string","description":"Colour theme of the app","enum":["light","dark"]},"background":{"type":"string","description":"Chat background"},"contacts":{"type":"array","description":"People to send a connection request to; each entry with a valid email or phone number gets one","items":{"$ref":"#/components/schemas/Contact"}},"timeZone":{"type":"string","description":"Time zone id","example":"Europe/Berlin"},"requestBiometricOnStart":{"type":"boolean","description":"Whether the app asks for biometric confirmation on start; true needs a biometric key enrolled on the calling device"},"notificationStatus":{"type":"string","description":"Master push-notification preference: AVAILABLE or DO_NOT_DISTURB","enum":["AVAILABLE","DO_NOT_DISTURB"]},"webAccessEnabled":{"type":"boolean","description":"Whether the account may be used on the web"}}},"UserSettingsDto":{"type":"object","description":"A user's persisted settings.","properties":{"currency":{"type":"string","description":"Code of the currency wallet balances are shown in","example":"USD"},"language":{"type":"string","description":"Language code the user reads the app and receives server messages in","example":"en"},"theme":{"type":"string","description":"Colour theme of the app","enum":["light","dark"]},"background":{"type":"string","description":"Chat background chosen by the user"},"timeZone":{"type":"string","description":"Time zone id of the user","example":"Europe/Berlin"},"requestBiometricOnStart":{"type":"boolean","description":"Whether the app asks for biometric confirmation when it starts"},"notificationStatus":{"type":"string","description":"Master push-notification preference: AVAILABLE, or DO_NOT_DISTURB which stops pushes","enum":["AVAILABLE","DO_NOT_DISTURB"]},"webAccessEnabled":{"type":"boolean","description":"Whether the account may be used on the web"},"walletFeaturesAvailable":{"type":"boolean","description":"Whether wallet features (deposits, sending, payment requests, withdrawals, conversions and app payments) are available to the user where they are now; false where such transactions are against the law, by the request's IP country or the phone number's country. Computed per request, not stored."}}},"WalletDepositAddressDto":{"type":"object","description":"The address that funds can be sent to for a wallet.","properties":{"walletId":{"type":"string","description":"Id of the wallet"},"walletType":{"type":"string","description":"The currency of the wallet","enum":["Bitcoin","Ethereum","USDBG"]},"address":{"type":"string","description":"The deposit address on the wallet's blockchain"},"createTime":{"type":"string","format":"date-time","description":"When the address was derived"}}},"WithdrawalQuoteDto":{"type":"object","description":"The fee a withdrawal would cost.","properties":{"walletId":{"type":"string","description":"Id of the wallet to withdraw from"},"walletType":{"type":"string","description":"The currency of the wallet","enum":["Bitcoin","Ethereum","USDBG"]},"amount":{"type":"number","description":"The amount asked about"},"networkFee":{"type":"number","description":"The network fee at this moment; it is worked out again when the withdrawal is made"},"totalDebit":{"type":"number","description":"The amount plus the fee, which the wallet would be debited"},"sufficientBalance":{"type":"boolean","description":"Whether the wallet's balance covers `totalDebit`"}}},"PagedModelWalletTransactionDto":{"type":"object","properties":{"content":{"type":"array","items":{"$ref":"#/components/schemas/WalletTransactionDto"}},"page":{"$ref":"#/components/schemas/PageMetadata"}}},"PagedModelWalletSwapDto":{"type":"object","properties":{"content":{"type":"array","items":{"$ref":"#/components/schemas/WalletSwapDto"}},"page":{"$ref":"#/components/schemas/PageMetadata"}}},"WalletSwapPairDto":{"type":"object","description":"A direction a conversion can be made in.","properties":{"fromWalletType":{"type":"string","description":"Currency converted from","enum":["Bitcoin","Ethereum","USDBG"]},"toWalletType":{"type":"string","description":"Currency converted into","enum":["Bitcoin","Ethereum","USDBG"]},"minAmountUsd":{"type":"number","description":"Smallest conversion, as the USD value of the amount converted"},"maxAmountUsd":{"type":"number","description":"Largest conversion, as the USD value of the amount converted"},"enabled":{"type":"boolean","description":"Whether the direction can be used at this moment"}}},"WalletLimitsDto":{"type":"object","description":"The caller's limits on withdrawals and transfers to other users, in US dollars.","properties":{"perTransactionUsd":{"type":"number","description":"The most one withdrawal or transfer may be worth; absent when the caller has no limit"},"dailyUsd":{"type":"number","description":"The most the caller's withdrawals and transfers together may be worth within the window; absent when the caller has no limit"},"windowHours":{"type":"integer","format":"int64","description":"The length of the window, in hours"},"usedUsd":{"type":"number","description":"What the caller's withdrawals and transfers in the window are worth"},"remainingUsd":{"type":"number","description":"What the caller may still send or withdraw in the window, never more than perTransactionUsd at once; absent when the caller has no limit"},"biometricRequiredAboveUsd":{"type":"number","description":"Above this a withdrawal or transfer needs biometric confirmation"},"biometricEnrolled":{"type":"boolean","description":"Whether the calling device has biometric confirmation turned on"},"deviceAvailableFrom":{"type":"string","format":"date-time","description":"While the calling device is in its waiting period, when it may send and withdraw; absent otherwise"},"unlimited":{"type":"boolean","description":"Whether an administrator removed this user's per-transaction and daily limits (proposal #133)"},"personalLimits":{"type":"boolean","description":"Whether the per-transaction or daily limit was set for this user by an administrator"},"personalLimitsExpireAt":{"type":"string","format":"date-time","description":"When the limits an administrator set for this user end; absent when they do not end or there are none"}}},"WalletLimitRequestStateDto":{"type":"object","description":"The higher sending limits a member may ask for, and their latest request.","properties":{"tiers":{"type":"array","description":"The tiers that may be asked for, the last being no limit at all","items":{"$ref":"#/components/schemas/WalletLimitTierDto"}},"durations":{"type":"array","description":"The durations that may be asked for","items":{"type":"string","enum":["DAYS_7","DAYS_30","UNTIL_CHANGED"]}},"request":{"$ref":"#/components/schemas/WalletLimitRequestDto","description":"The open request, or the latest one decided within the last 7 days; absent otherwise"},"requestAllowedFrom":{"type":"string","format":"date-time","description":"After a refusal, when a new request may be made; absent when one may be made now (or one is open)"}}},"WalletLimitTierDto":{"type":"object","description":"Higher sending limits a member may ask for, in US dollars.","properties":{"name":{"type":"string","description":"The tier's name, to give in a request: plus, pro, business or no-limit"},"perTransactionUsd":{"type":"number","description":"The most one withdrawal or transfer may be worth; absent for no-limit"},"dailyUsd":{"type":"number","description":"The most withdrawals and transfers together may be worth within the window; absent for no-limit"},"unlimited":{"type":"boolean","description":"Whether this is no limit at all"}}},"WalletBalanceDto":{"type":"object","description":"The balance of a wallet.","properties":{"id":{"type":"string","description":"Id of the balance record"},"createTime":{"type":"string","format":"date-time","description":"When the balance record was created"},"userId":{"type":"string","description":"Id of the wallet's owner"},"walletId":{"type":"string","description":"Id of the wallet"},"balance":{"type":"number","description":"Current balance, in the wallet's native unit"}}},"UserProfileDto":{"type":"object","description":"Another user's public profile: when they joined and how active they are in public rooms.","properties":{"id":{"type":"string","description":"Unique id of the user","example":"2bTBIYWDF7nl1mcJ2KRbOSIuM16"},"name":{"type":"string","description":"Display name"},"memberSince":{"type":"string","format":"date-time","description":"When the account was created; the app shows it as a month and year"},"publicPosts":{"type":"integer","format":"int64","description":"Messages the user has posted in public rooms","example":128},"publicComments":{"type":"integer","format":"int64","description":"Comments the user has written in public rooms","example":41},"publicRooms":{"type":"integer","format":"int64","description":"Public groups the user is currently a member of","example":6},"connected":{"type":"boolean","description":"Whether the caller and this user are connected"}}},"PagedModelUserMinimalDto":{"type":"object","properties":{"content":{"type":"array","items":{"$ref":"#/components/schemas/UserMinimalDto"}},"page":{"$ref":"#/components/schemas/PageMetadata"}}},"PagedModelUserConnectionRequestDto":{"type":"object","properties":{"content":{"type":"array","items":{"$ref":"#/components/schemas/UserConnectionRequestDto"}},"page":{"$ref":"#/components/schemas/PageMetadata"}}},"UserConnectionRequestDto":{"type":"object","description":"A request to connect, with the details of its sender.","properties":{"id":{"type":"string","description":"Id of the connection request"},"fromUserId":{"type":"string","description":"Id of the user who sent the request"},"fromUser":{"$ref":"#/components/schemas/UserDetails","description":"Details of the user who sent the request"},"toEmail":{"type":"string","description":"Email address the request was sent to, when it was sent to an email address"},"toPhone":{"type":"string","description":"Phone number the request was sent to, when it was sent to a phone number"},"toUserId":{"type":"string","description":"Id of the user the request was sent to, when it was sent by user id; the phone is then left out"},"toUserName":{"type":"string","description":"Name of the person the request was sent to"},"enabled":{"type":"boolean","description":"Whether the request is still open"},"createTime":{"type":"string","format":"date-time","description":"When the request was sent"},"message":{"type":"string","description":"Personal note from the sender"},"denyReason":{"type":"string","description":"Reason given when the request was declined","enum":["UNKNOWN","SPAM","HARASSMENT","INAPPROPRIATE_CONTENT","DONT_KNOW_PERSON","OTHER"]}}},"UserDetails":{"type":"object","description":"The user who sent a connection request.","properties":{"id":{"type":"string","description":"Id of the sender"},"email":{"type":"string","description":"Email address of the sender"},"phone":{"type":"string","description":"Phone number of the sender"},"name":{"type":"string","description":"Display name of the sender"}}},"PagedModelUserBlockDto":{"type":"object","properties":{"content":{"type":"array","items":{"$ref":"#/components/schemas/UserBlockDto"}},"page":{"$ref":"#/components/schemas/PageMetadata"}}},"PageObject":{"type":"object","properties":{"totalElements":{"type":"integer","format":"int64"},"totalPages":{"type":"integer","format":"int32"},"size":{"type":"integer","format":"int32"},"content":{"type":"array","items":{"$ref":"#/components/schemas/RoomMessageDto"}},"number":{"type":"integer","format":"int32"},"pageable":{"$ref":"#/components/schemas/PageableObject"},"sort":{"$ref":"#/components/schemas/SortObject"},"last":{"type":"boolean"},"first":{"type":"boolean"},"numberOfElements":{"type":"integer","format":"int32"},"empty":{"type":"boolean"}}},"PageableObject":{"type":"object","properties":{"offset":{"type":"integer","format":"int64"},"paged":{"type":"boolean"},"pageNumber":{"type":"integer","format":"int32"},"pageSize":{"type":"integer","format":"int32"},"sort":{"$ref":"#/components/schemas/SortObject"},"unpaged":{"type":"boolean"}}},"RoomAccessDto":{"type":"object","description":"The caller's membership of a room.","properties":{"id":{"type":"string","description":"Id of the membership"},"createTime":{"type":"string","format":"date-time","description":"When the caller joined"},"roomId":{"type":"string","description":"Id of the room"},"userId":{"type":"string","description":"Id of the member"},"unreadRoomMessages":{"type":"integer","format":"int32","description":"Number of messages the member has not read yet"},"enabled":{"type":"boolean","description":"Whether the membership is active"},"muted":{"type":"boolean","description":"Whether the member has muted the room"},"disabledByAdmin":{"type":"boolean","description":"Whether the room's admin removed the member's access"},"publishEnabled":{"type":"boolean","description":"Whether the member may post in the room"}}},"RoomDetailsDto":{"type":"object","description":"A room with what the room screen shows.","properties":{"room":{"$ref":"#/components/schemas/RoomExtendedDto","description":"The room"},"roomAccess":{"$ref":"#/components/schemas/RoomAccessDto","description":"The caller's own membership of the room"},"roomParticipants":{"type":"object","additionalProperties":{"$ref":"#/components/schemas/UserMinimalDto"},"description":"The participants, by user id. Every member for direct and private group rooms; only the creator and the admin for public groups (the member count is the room's userCount); the authors of the shown messages for the broadcast room"},"roomMessages":{"$ref":"#/components/schemas/PageObject","description":"A page of the latest messages"}}},"RoomExtendedDto":{"type":"object","description":"A chat room with the caller's own state in it.","properties":{"id":{"type":"string","description":"Unique id of the room","example":"2bTBIYWDF7nl1mcJ2KRbOSIuM16"},"name":{"type":"string","description":"Name of the room"},"description":{"type":"string","description":"Description of the room"},"creatorUserId":{"type":"string","description":"Id of the user who created the room"},"adminUserId":{"type":"string","description":"Id of the user who administers the room"},"roomType":{"type":"string","description":"Kind of room, such as a direct chat, a private or public group, or a broadcast room","enum":["PRIVATE_DIRECT","PRIVATE_GROUP","PUBLIC_GROUP","PUBLIC_BROADCAST"]},"createTime":{"type":"string","format":"date-time","description":"When the room was created"},"encryptionKey":{"type":"string","description":"The room's AES key, encrypted with the calling device's RSA public key and Base64 encoded. Absent for end-to-end encrypted rooms, whose keys only the clients hold"},"directIdPair":{"type":"string","description":"For a direct chat, the ids of its two users separated by a colon"},"userOneAvatarUrl":{"type":"string","description":"For a direct chat, avatar URL of the first user"},"userTwoAvatarUrl":{"type":"string","description":"For a direct chat, avatar URL of the second user"},"roomAvatarUrl":{"type":"string","description":"Avatar URL of the room"},"newMessages":{"type":"integer","format":"int64","description":"Number of messages the caller has not read yet"},"userCount":{"type":"integer","format":"int32","description":"Number of members"},"postCount":{"type":"integer","format":"int32","description":"Number of messages posted in the room"},"muted":{"type":"boolean","description":"Whether the caller has muted the room"},"participantPublishEnabled":{"type":"boolean","description":"Whether members other than the creator and admin may post"},"commentsEnabled":{"type":"boolean","description":"Whether members may comment on messages"},"e2ee":{"type":"boolean","description":"Whether the room is end-to-end encrypted"},"e2eeKeyVersion":{"type":"integer","format":"int32","description":"Current version of the room key; below 1 until the first key exists"},"e2eeRotationRequired":{"type":"boolean","description":"Whether a member or device that held the room key lost access, so a device that holds the key should create the next version"}}},"RoomMessageDto":{"type":"object","description":"A message in a room.","properties":{"id":{"type":"string","description":"Id of the message"},"roomId":{"type":"string","description":"Id of the room"},"creatorUserId":{"type":"string","description":"Id of the author"},"creatorUserName":{"type":"string","description":"Display name of the author"},"expireOn":{"type":"string","format":"date-time","description":"Optional expiry time of the message. Not used by the server at present"},"activeOn":{"type":"string","format":"date-time","description":"Optional time the message becomes active. Not used by the server at present"},"text":{"type":"string","description":"The message text; for an end-to-end encrypted room, the encrypted payload"},"createTime":{"type":"string","format":"date-time","description":"When the message was posted"},"receiverUserId":{"type":"string","description":"When set, the message is addressed to this member only"},"fromBot":{"type":"boolean","description":"Whether a bot posted the message"},"containsAttachment":{"type":"boolean","description":"Whether the message has attachments"},"containsEmoji":{"type":"boolean","description":"Whether the message has emoji reactions"},"containsCard":{"type":"boolean","description":"Whether the message carries an app card"},"appId":{"type":"string","description":"Id of the app that posted the message on its own, such as a feed; absent for a person's message"},"messageIndex":{"type":"integer","format":"int32","description":"Position of the message in the room, counting up from 1"},"commentCount":{"type":"integer","format":"int32","description":"Number of comments on the message"},"emojis":{"type":"object","additionalProperties":{"type":"array","items":{"$ref":"#/components/schemas/UserShortDto"}},"description":"Reactions on the message: each emoji with the users who chose it. Absent when there are none"},"mentions":{"type":"object","additionalProperties":{"type":"string"},"description":"Mentions in the text: `@User Name` or `#Some Room` mapped to the id of that user or room"}}},"SortObject":{"type":"object","properties":{"empty":{"type":"boolean"},"sorted":{"type":"boolean"},"unsorted":{"type":"boolean"}}},"Resolution":{"type":"object","description":"The room a shared link points at.","properties":{"status":{"type":"string","description":"FOUND when the key is the room's current slug, REDIRECT when it identifies a public room by another key (room id, old slug or other case), GONE when it was the slug of a deleted room, NOT_FOUND otherwise","enum":["FOUND","REDIRECT","GONE","NOT_FOUND"]},"roomId":{"type":"string","description":"Id of the room; set for FOUND and REDIRECT"},"slug":{"type":"string","description":"The room's current slug; set for FOUND and REDIRECT"}}},"SlugCheck":{"type":"object","description":"Whether a public room name can be used.","properties":{"available":{"type":"boolean","description":"Whether the name can be used"},"slug":{"type":"string","description":"The readable link key the name maps to; absent when the name has no usable one"},"message":{"type":"string","description":"Localized reason the name cannot be used; absent when it can"}}},"PagedModelPaymentRequestDto":{"type":"object","properties":{"content":{"type":"array","items":{"$ref":"#/components/schemas/PaymentRequestDto"}},"page":{"$ref":"#/components/schemas/PageMetadata"}}},"PagedModelPaymentRequestItemDto":{"type":"object","properties":{"content":{"type":"array","items":{"$ref":"#/components/schemas/PaymentRequestItemDto"}},"page":{"$ref":"#/components/schemas/PageMetadata"}}},"PagedModelRoomMessageAttachmentDto":{"type":"object","properties":{"content":{"type":"array","items":{"$ref":"#/components/schemas/RoomMessageAttachmentDto"}},"page":{"$ref":"#/components/schemas/PageMetadata"}}},"RoomMessageAttachmentDto":{"type":"object","description":"A file attached to a message.","properties":{"id":{"type":"string","description":"Id of the attachment"},"roomId":{"type":"string","description":"Id of the room"},"roomMessageId":{"type":"string","description":"Id of the message the file belongs to"},"creatorUserId":{"type":"string","description":"Id of the user who uploaded the file"},"fileName":{"type":"string","description":"Original file name"},"fileSize":{"type":"integer","format":"int64","description":"Size of the file in bytes"},"fileType":{"type":"string","description":"Kind of file, which decides how it is processed and shown","enum":["IMAGE","VIDEO","AUDIO","OTHER"]},"createTime":{"type":"string","format":"date-time","description":"When the file was uploaded"},"encrypted":{"type":"boolean","description":"Whether the file is an end-to-end encrypted blob the server cannot read"}}},"PagedModelRoomMessageDto":{"type":"object","properties":{"content":{"type":"array","items":{"$ref":"#/components/schemas/RoomMessageDto"}},"page":{"$ref":"#/components/schemas/PageMetadata"}}},"E2eeDeviceDto":{"type":"object","description":"A device that a room key can be wrapped for.","properties":{"userId":{"type":"string","description":"Id of the device's owner"},"deviceId":{"type":"string","description":"Id of the device"},"x25519PublicKey":{"type":"string","description":"Public key that room keys are wrapped to"},"ed25519PublicKey":{"type":"string","description":"Public key that verifies the shares the device sends"},"fingerprint":{"type":"string","description":"Fingerprint of the device's keys, to compare between devices"},"deviceName":{"type":"string","description":"Name of the device"},"platform":{"type":"string","description":"Platform of the device"},"createTime":{"type":"string","format":"date-time","description":"When the device was registered"}}},"E2eeMissingShareDto":{"type":"object","description":"A device that lacks room keys.","properties":{"device":{"$ref":"#/components/schemas/E2eeDeviceDto","description":"The device"},"missingVersions":{"type":"array","description":"The key versions it has no copy of","items":{"type":"integer","format":"int32"}}}},"E2eeKeyShareDto":{"type":"object","description":"A copy of a room key wrapped for one device.","properties":{"keyVersion":{"type":"integer","format":"int32","description":"Version of the room key"},"wrappedKey":{"type":"string","description":"The room key, wrapped for the device"},"wrapAlgorithm":{"type":"string","description":"Algorithm used to wrap the key"},"senderUserId":{"type":"string","description":"Id of the user whose device wrapped the key"},"senderDeviceId":{"type":"string","description":"Id of the device that wrapped the key"},"senderEd25519PublicKey":{"type":"string","description":"Public key that verifies `senderSignature`"},"senderSignature":{"type":"string","description":"Signature of the sender over the share"}}},"E2eeKeySharesDto":{"type":"object","description":"The room key copies stored for the calling device.","properties":{"deviceId":{"type":"string","description":"Id of the calling device"},"keyVersion":{"type":"integer","format":"int32","description":"The room's current key version"},"rotationRequired":{"type":"boolean","description":"Whether the key should be replaced, because someone or a device that held it lost access"},"shares":{"type":"array","description":"The copies for the calling device, for every key version","items":{"$ref":"#/components/schemas/E2eeKeyShareDto"}}}},"E2eeKeyBackupDto":{"type":"object","description":"A user's passphrase-protected backup of their room keys, exactly as they uploaded it.","properties":{"backupVersion":{"type":"integer","format":"int64","description":"Version of the backup; it only ever grows"},"kdf":{"type":"string","description":"Name of the key derivation function"},"kdfParams":{"type":"string","description":"Parameters of the key derivation function, opaque to the server"},"ciphertext":{"type":"string","description":"The encrypted backup, opaque to the server"},"updateTime":{"type":"string","format":"date-time","description":"When the backup was stored"}}},"DeviceSummaryDto":{"type":"object","description":"One of the caller's registered devices. Never contains key material.","properties":{"id":{"type":"string","description":"Id of the device"},"deviceName":{"type":"string","description":"Name of the device, as reported by the app"},"platform":{"type":"string","description":"Platform of the device, such as ios or android"},"lastSeen":{"type":"string","format":"date-time","description":"When the device last made a request"},"lastIp":{"type":"string","description":"IP address of the device's last request"},"lastLocation":{"type":"string","description":"Where the device's last IP address places it, such as \"Paris, France\", in the caller's language; null when the address cannot be placed"},"trustLevel":{"type":"string","description":"UNVERIFIED, TRUSTED_DEVICE or BLOCKED","enum":["UNVERIFIED","TRUSTED_DEVICE","BLOCKED"]},"biometricEnrolled":{"type":"boolean","description":"Whether a biometric signing key is enrolled on the device"},"revoked":{"type":"boolean","description":"Whether the device has been revoked and can no longer sign in"},"currentDevice":{"type":"boolean","description":"Whether this is the device the request came from"},"fingerprint":{"type":"string","description":"Lower-case hex SHA-256 of the device's public key, to compare with what another device of the user shows"}}},"PagedModelMessageCommentDto":{"type":"object","properties":{"content":{"type":"array","items":{"$ref":"#/components/schemas/MessageCommentDto"}},"page":{"$ref":"#/components/schemas/PageMetadata"}}},"ChatBackgroundDto":{"type":"object","description":"A chat background the user can pick.","properties":{"name":{"type":"string","description":"Display name of the background"},"imageUrl":{"type":"string","description":"URL of the background image"}}},"ChatStatusDto":{"type":"object","description":"Everything the app loads when it starts.","properties":{"currentUser":{"$ref":"#/components/schemas/UserDto","description":"The caller's profile"},"rooms":{"type":"array","description":"The caller's rooms","items":{"$ref":"#/components/schemas/RoomExtendedDto"}},"newMessagesMap":{"type":"object","additionalProperties":{"type":"integer","format":"int32"},"description":"Number of unread messages by room id"},"contacts":{"type":"array","description":"The caller's connections","items":{"$ref":"#/components/schemas/UserMinimalDto"}},"userSettings":{"$ref":"#/components/schemas/UserSettingsDto","description":"The caller's settings"},"chatBackgrounds":{"type":"array","description":"Chat backgrounds the user can pick","items":{"$ref":"#/components/schemas/ChatBackgroundDto"}},"bitcoinMainNetUsed":{"type":"boolean","description":"Whether the server uses the Bitcoin main network, as opposed to a test network"},"ethereumMainNetUsed":{"type":"boolean","description":"Whether the server uses the Ethereum main network, as opposed to a test network"}}},"IceConfigResponse":{"type":"object","description":"The ICE servers for a WebRTC connection.","properties":{"iceServers":{"type":"array","description":"The STUN and TURN servers to use","items":{"$ref":"#/components/schemas/IceServer"}}}},"IceServer":{"type":"object","description":"A STUN or TURN server.","properties":{"urls":{"type":"array","description":"URLs of the server","items":{"type":"string"}},"username":{"type":"string","description":"Username for the server; set for TURN servers, whose credentials are generated for the caller"},"credential":{"type":"string","description":"Credential for the server; set for TURN servers"}}},"JwksDto":{"type":"object","description":"The public keys that verify cards and web-app session tokens signed by the server (RFC 7517).","properties":{"keys":{"type":"array","description":"The keys, each an OKP/Ed25519 JSON Web Key with its kid","items":{"type":"object","additionalProperties":{}}}}},"PagedModelAppPaymentDto":{"type":"object","properties":{"content":{"type":"array","items":{"$ref":"#/components/schemas/AppPaymentDto"}},"page":{"$ref":"#/components/schemas/PageMetadata"}}},"PagedModelAppDto":{"type":"object","properties":{"content":{"type":"array","items":{"$ref":"#/components/schemas/AppDto"}},"page":{"$ref":"#/components/schemas/PageMetadata"}}},"BiometricKeyRemoveRequest":{"type":"object","description":"Proof for removing the biometric key of the calling device: a signature from the key, or an SMS code.","properties":{"nonce":{"type":"string","description":"Single-use value that the signature covers"},"signature":{"type":"string","description":"Signature of BIOMETRIC_KEY_CHANGE_V1|REMOVE|<nonce> made with the enrolled key"},"code":{"type":"string","description":"Instead of a signature: the code sent by POST /api/devices/biometric-key/code"}}}},"securitySchemes":{"BearerAuth":{"type":"http","description":"Access token (JWT) issued by `POST /api/auth/otp/verify` or `POST /api/auth/token/refresh`, sent as `Authorization: Bearer <token>`.","scheme":"bearer","bearerFormat":"JWT"}}}}