.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\\*][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-user-categories-delimited: &work-user-categories-delimited description: |- Custom categories of 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 name 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 the building name, 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' 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 default: firstName enum: - firstName - lastName 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= ) ) ``` 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 category: *work-user-categories-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 (display) name. 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/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. 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.