.references: work-servers: &work-servers - url: https://ds-apip-work.threema.ch description: Production server - url: https://ds-apip-work.test.threema.ch description: Sandbox server base64: &base64 type: string format: byte u64: &u64 type: integer minimum: 0 maximum: 18446744073709551615 identity: &identity description: A Threema ID. type: string minLength: 8 maxLength: 8 pattern: ^[0-9A-Z*]{1}[0-9A-Z]{7}$ example: ECHOECHO public-key: &public-key <<: *base64 description: Public Key (32 bytes, base64) associated to a Threema ID. example: ZWNob2VjaG9lY2hvZWNob2VjaG9lY2hvZWNob2VjaG8= app-version: &app-version description: |- App version. This is the CSP `client-info` but with the following special postfix: 1. If at least one Threema-MDM parameter and at least one external MDM parameter is active, append `;me` 2. If at least one Threema-MDM parameter is active, append `;m` 3. If at least one external MDM parameter is active, append `;e` 4. If no MDM parameter is active, don't append. (A lone `;` is also acceptable.) type: string minLength: 1 example: 1.2.3;Q;de/DE;... work-username: &work-username description: Work license username. type: string minLength: 1 example: echoecho@threema.ch work-password: &work-password description: Work license password. type: string minLength: 1 example: super-secret-password work-nickname: &work-nickname description: |- User's nickname sourced from the MDM parameter `th_nickname`. Empty is equivalent to unset. type: string example: personal-🦜 work-first-name: &work-first-name description: |- User's first name sourced from the MDM parameter `th_firstname`. Empty is equivalent to unset. type: string example: Aria work-last-name: &work-last-name description: |- User's last name sourced from the MDM parameter `th_lastname`. Empty is equivalent to unset. type: string example: Reverb work-user-identifier: &work-user-identifier description: |- Custom unique identifier of the user (e.g. employee number), sourced from the MDM parameter `th_csi`. Also known as _Customer Specific Identifier_. Empty is equivalent to unset. type: string work-job-title: &work-job-title description: |- Job title of the user, sourced from the MDM parameter `th_job_title`. Empty is equivalent to unset. type: string work-department: &work-department description: |- Department the user is working in, sourced from the MDM parameter `th_department`. Empty is equivalent to unset. type: string work-user-category-labels-delimited: &work-user-category-labels-delimited description: |- Custom category labels assigned to the user (e.g. building name, room number), sourced from the MDM parameter `th_category`. Empty is equivalent to unset. Note: For compatibility with the app configuration parameter `th_category`, this is the delimited variant with the category label separated by the category delimiter (`,` by default). type: string example: Building 1, Room 337 work-user-category-id: &work-user-category-id description: A custom category ID. type: string minLength: 1 example: 'jNekOWhQ8B' work-user-category-ids: &work-user-category-ids description: |- Custom category IDs of the user (e.g. mapping to a corresponding label such as building name or room number), chosen by the Work subscription administrator. type: array items: *work-user-category-id example: - '1' - 'jNekOWhQ8B' work-contacts-match: &work-contacts-match type: array items: *identity example: - ECHOECHO - '*SUPPORT' work-contact: &work-contact type: object required: - id - pk properties: id: *identity pk: *public-key first: <<: *work-first-name type: - string - 'null' last: <<: *work-last-name type: - string - 'null' jobTitle: *work-job-title department: *work-department work-organisation: &work-organisation type: object required: - name properties: name: description: |- Optional name of the organisation, translated by the `Accept-Language` header (if provided). type: - string - 'null' work-directory-page-index: &work-directory-page-index description: Page index. type: integer minimum: 0 work-directory-sort: &work-directory-sort type: object properties: by: description: The sort key applied to the resulting contacts. type: string anyOf: - title: First name const: firstName - title: Last name const: lastName - {} default: firstName asc: description: |- Sort contacts ascending by `by` if `true`, sort descending if `false`. type: boolean default: true auth-challenge-request: &auth-challenge-request description: Authentication required. type: object required: - token - tokenRespKeyPub properties: token: <<: *base64 description: An arbitrary challenge token (base64) to be _signed_. tokenRespKeyPub: <<: *base64 description: |- The ephemeral public key (EPK, 32 bytes, base64) to derive a shared secret for solving the challenge. auth-challenge-response: &auth-challenge-response type: object required: - token - response properties: token: <<: *base64 description: The token of the challenge request (base64). response: <<: *base64 description: |- The token of the challenge request, _signed_ in the following way: ```text base64( BLAKE2b( key=BLAKE2b( key=X25519HSalsa20(CK.secret, EPK.public), salt='dir', personal='3ma-csp', ), input= ) ) ``` blob-credentials-request: &blob-credentials-request type: object required: - identity properties: identity: *identity blob-credentials-response: ok: &blob-credentials-response-ok description: |- Blob server authentication credentials retrieved successfully. type: object required: - success - token - expiration properties: success: type: boolean const: true token: description: |- An opaque token used to authenticate against the blob server. HTTP requests towards the the blob server must include it as a header in the following way: `Authorization: ThreemaBlobToken ` type: string expiration: <<: *u64 description: |- Amount of seconds until the token expires and must be discarded. example: success: true token: givemeaccessplz expiration: 600 error: &blob-credentials-response-error description: |- Blob server authentication credentials could not be retrieved. type: object required: - success properties: success: type: boolean const: false error: type: string example: sucess: false error: 'Identity not found' id-revocation-request: &id-revocation-request type: object required: - identity properties: identity: *identity id-revocation-key-v1: &id-revocation-key-v1 <<: *base64 minLength: 8 maxLength: 8 description: |- A legacy (v1) identity revocation key. The revocation key is computed as follows (`[:4]` denotes the first four bytes): ```text base64( SHA256(revocation-password)[:4] ) ``` id-revocation-request-v1: &id-revocation-request-v1 type: object required: - identity - revocationKey properties: identity: *identity revocationKey: *id-revocation-key-v1 id-revocation-key-check-response: found: &id-revocation-key-check-response-found description: 'ID revocation key was set' required: - revocationKeySet - lastChanged properties: revocationKeySet: type: boolean const: true lastChanged: type: string format: date-time example: revocationKeySet: true lastChanged: '2014-10-29T12:32:54Z' empty: &id-revocation-key-check-response-empty description: 'ID revocation key was not set' required: - revocationKeySet properties: revocationKeySet: type: boolean const: false id-revocation-response: ok: &id-revocation-response-ok description: 'ID revocation key request was successful' type: object required: - success properties: success: type: boolean const: true error: &id-revocation-response-error description: 'ID revocation key was not successful' type: object required: - success properties: success: type: boolean const: false error: type: string example: sucess: false error: 'Identity not found' sfu-credentials-request: &sfu-credentials-request type: object required: - identity properties: identity: *identity sfu-credentials-response: ok: &sfu-credentials-response-ok description: SFU information retrieved successfully. type: object required: - success - sfuBaseUrl - allowedSfuHostnameSuffixes - sfuToken - expiration properties: success: type: boolean const: true sfuBaseUrl: description: |- Base URL used to create and distribute new calls. type: string allowedSfuHostnameSuffixes: description: |- A set of allowed hostname suffixes to be applied against a _SFU Base URL_ when joining calls. If the provided _SFU Base URL_'s hostname does not end with one of the provided hostname suffixes, joining or peeking that call is disallowed. type: array items: type: string sfuToken: description: |- An opaque token used to authenticate against a SFU. HTTP requests towards the SFU must include it as a header in the following way: `Authorization: ThreemaSfuToken ` type: string expiration: <<: *u64 description: |- Amount of seconds until the SFU information is considered stale and must be discarded. example: success: true sfuBaseUrl: https://sfu.threema.ch allowedSfuHostnameSuffixes: - threema.ch sfuToken: givemeaccessplz expiration: 600 error: &sfu-credentials-response-error description: SFU information could not be retrieved. type: object required: - success properties: success: type: boolean const: false error: type: string example: sucess: false error: 'Identity not found' update-work-data-request: &update-work-data-request type: object required: - identity - licenseUsername - licensePassword - version properties: identity: *identity licenseUsername: *work-username licensePassword: *work-password version: *app-version publicNickname: *work-nickname firstName: *work-first-name lastName: *work-last-name csi: *work-user-identifier jobTitle: *work-job-title department: *work-department category: *work-user-category-labels-delimited update-work-data-response: ok: &update-work-data-response-ok description: Work data updated successfully. type: object required: - success properties: success: type: boolean const: true error: &update-work-data-response-error description: Updating Work data failed. type: object required: - success properties: success: type: boolean const: false error: type: string example: sucess: false error: 'Missing parameters' work-auth: &work-auth type: object required: - username - password properties: username: *work-username password: *work-password sync-work-data-request: &sync-work-data-request type: object required: - contacts properties: contacts: <<: *work-contacts-match description: |- A list of all existing contacts of the user to match against the Work subscription. Note: This is necessary to determine whether a contact is part of the user's Work subscription and, in that case, get additional information. Note 2: Explicitly providing all of the user's contacts also prevents having to configure **all** Work contacts of the same subscription. sync-work-data-response: &sync-work-data-response type: object required: - checkInterval - org - logo - support - directory - mdm - contacts properties: checkInterval: <<: *u64 description: |- Target amount of seconds until a subsequent Work sync should be initiated. org: *work-organisation logo: description: Logo to be displayed in the app. type: object required: - light - dark properties: light: &work-logo-url description: |- Optional URL to a logo to be displayed in the app. The logo must be provided in PNG format. type: - string - 'null' dark: *work-logo-url support: description: Optional custom in-app support base URL. type: - string - 'null' directory: oneOf: - description: Disabled Work directory. type: object required: - enabled properties: enabled: type: boolean const: false - description: Enabled Work directory. type: object required: - enabled - cat properties: enabled: type: boolean const: true cat: description: |- Map of contact category IDs to their respective label. type: object additionalProperties: *work-user-category-id mdm: description: App configuration to be applied. type: object required: - override - params properties: override: description: |- Whether the app configuration parameters provided here take precedence over the the externally configured MDM parameters. type: boolean params: description: |- A key/value map of app configuration / MDM parameters as defined by the protocol. type: object additionalProperties: oneOf: - type: string - *u64 - type: boolean contacts: description: |- A list of contacts from the same Work subscription to be configured on the user's device. type: array items: <<: *work-contact description: A configured Work contact. work-contacts-request: &work-contacts-request type: object required: - contacts properties: contacts: <<: *work-contacts-match description: |- A list of contacts (Threema IDs) to get additional Work properties for. Note: This is necessary to determine whether a contact is part of the user's Work subscription and, in that case, get additional information. work-contacts-response: &work-contacts-response type: object required: - contacts properties: contacts: description: |- A subset of the provided contacts that are part of the same Work subscription with the associated additional Work properties. type: array items: <<: *work-contact description: A Work contact. work-directory-request-wildcard: &work-directory-request-wildcard type: object required: - query - page - cateogries properties: identity: <<: *identity description: The user's Threema ID. page: *work-directory-page-index query: description: Wildcard search query. type: string const: '*' categories: <<: *work-user-category-ids description: At least one category ID to narrow down the search with. minLength: 1 sort: *work-directory-sort work-directory-request-specific: &work-directory-request-specific type: object required: - query - page properties: identity: <<: *identity description: The user's Threema ID. page: *work-directory-page-index query: description: |- Search query. Matches any of Threema ID, first name, or last name. type: string minLength: 3 example: Bob categories: <<: *work-user-category-ids description: Optional category IDs to narrow down the search with. sort: *work-directory-sort work-directory-response: &work-directory-response type: object required: - contacts - paging properties: paging: description: Page information. type: object required: - size - total properties: size: description: Maximum amount of results present in a single page. type: integer minimum: 0 total: description: Total amount of results (spread across pages). type: integer minimum: 0 prev: description: Previous page index, if any is available. type: integer minimum: 0 next: description: Next page index, if any is available. type: integer minimum: 1 contacts: description: |- A Work contact of the same subcription that matches the search query. type: array items: !merge-objects - *work-contact - type: object required: - org properties: csi: *work-user-identifier cat: *work-user-category-ids org: *work-organisation openapi: 3.1.0 info: title: Directory and Work Sync Server API description: |- Maintains the directory of allocated Threema IDs and all associated properties. version: 1.0.0 servers: - url: https://ds-apip.threema.ch description: Production server - url: https://ds-apip.test.threema.ch description: Sandbox server paths: /identity/blob_cred: post: summary: Blob Server Credentials description: |- Retrieve blob server authentication credentials. The first call without the challenge response properties initiates the challenge request. The second call must repeat the exact same properties and the challenge response. requestBody: required: true content: application/json: schema: oneOf: - *blob-credentials-request - !merge-objects - *auth-challenge-response - *blob-credentials-request responses: '200': description: Success... or not. content: application/json: schema: oneOf: - *auth-challenge-request - *blob-credentials-response-ok - *blob-credentials-response-error '429': description: Rate limit exceeded. /identity/revoke: post: summary: ID Revocation by Client Key description: |- Revoke a Threema ID by proofing the knowledge of the client key. The first call without the challenge response properties initiates the challenge request. The second call must repeat the exact same properties and the challenge response. requestBody: required: true content: application/json: schema: oneOf: - *id-revocation-request - !merge-objects - *id-revocation-request - *auth-challenge-response responses: '200': description: Success... or not. content: application/json: schema: oneOf: - *auth-challenge-request - *id-revocation-response-ok - *id-revocation-response-error /identity/set_revocation_key: post: summary: Set ID Revocation Key description: |- Set the revocation key for an identity. The first call without the challenge response properties initiates the challenge request. The second call must repeat the exact same properties and the challenge response. requestBody: required: true content: application/json: schema: oneOf: - *id-revocation-request-v1 - !merge-objects - *id-revocation-request-v1 - *auth-challenge-response responses: '200': description: Success... or not. content: application/json: schema: oneOf: - *auth-challenge-request - *id-revocation-response-ok - *id-revocation-response-error /identity/check_revocation_key: post: summary: Check ID Revocation Key description: |- Check whether a revocation key is set for a given ID The first call without the challenge response properties initiates the challenge request. The second call must repeat the exact same properties and the challenge response. requestBody: required: true content: application/json: schema: oneOf: - *id-revocation-request - !merge-objects - *id-revocation-request-v1 - *auth-challenge-response responses: '200': description: Success... or not. content: application/json: schema: oneOf: - *auth-challenge-request - *id-revocation-response-error - *id-revocation-key-check-response-found - *id-revocation-key-check-response-empty /identity/ws/revoke: post: summary: ID Revocation by User-set Key description: Revoke a Threema ID with a user-set key previously derived from a password. requestBody: required: true content: application/json: schema: oneOf: - *id-revocation-request-v1 responses: '200': description: Success... or not. content: application/json: schema: oneOf: - *id-revocation-response-ok - *id-revocation-response-error /identity/sfu_cred: post: summary: SFU Information description: |- Retrieve SFU information including URLs and authentication credentials. The first call without the challenge response properties initiates the challenge request. The second call must repeat the exact same properties and the challenge response. requestBody: required: true content: application/json: schema: oneOf: - *sfu-credentials-request - !merge-objects - *auth-challenge-response - *sfu-credentials-request responses: '200': description: Success... or not. content: application/json: schema: oneOf: - *auth-challenge-request - *sfu-credentials-response-ok - *sfu-credentials-response-error '429': description: Rate limit exceeded. /identity/update_work_info: post: summary: Work Properties description: |- Update Work properties associated to the currently used Threema ID. Only used by the _Work_ flavour of Threema. The first call without the challenge response properties initiates the challenge request. The second call must repeat the exact same properties and the challenge response. Note that all data of the request must be sourced **exclusively** from MDM parameters. For example, the data source for `nickname` must be `th_nickname` and not the custom nickname chosen by the user. TODO(SE-368): When sending/receiving steps. requestBody: required: true content: application/json: schema: oneOf: - *update-work-data-request - !merge-objects - *auth-challenge-response - *update-work-data-request responses: '200': description: Success... or not. content: application/json: schema: oneOf: - *auth-challenge-request - *update-work-data-response-ok - *update-work-data-response-error '429': description: Rate limit exceeded. /fetch2: post: summary: Work Sync description: |- Full sync of all data associated to the Work subscription. Only used by the _Work_ flavour of Threema. TODO(SE-368): When sending/receiving steps. servers: *work-servers requestBody: required: true content: application/json: schema: !merge-objects - *work-auth - *sync-work-data-request responses: '200': description: Work subscription data. content: application/json: schema: *sync-work-data-response '400': description: Invalid request. '401': description: Invalid username or password. '429': description: Rate limit exceeded. /identities: post: summary: Work Contacts description: |- Request properties associated to a contact of the same Work subscription. Note: This endpoint is currently buggy. See TWRK-1633 for a list of bugs. TODO(SE-368): When sending/receiving steps. Send before adding a new contact. servers: *work-servers requestBody: required: true content: application/json: schema: !merge-objects - *work-auth - *work-contacts-request responses: '200': description: Matching Work contacts in the same Work subscription. content: application/json: schema: *work-contacts-response '400': description: Invalid request. '401': description: Invalid username or password. '429': description: Rate limit exceeded. /directory: post: summary: Work Directory description: |- Search for contacts in the same Work subscription as the user. TODO(SE-368): When sending/receiving steps. servers: *work-servers requestBody: required: true content: application/json: schema: oneOf: - !merge-objects - *work-auth - *work-directory-request-wildcard - !merge-objects - *work-auth - *work-directory-request-specific responses: '200': description: Queried Work contacts of the same Work subscription. content: application/json: schema: *work-directory-response '400': description: Invalid request. '401': description: Invalid username or password. '429': description: Rate limit exceeded.