Parcourir la source

docs(api): 添加 Celestia Trace 后端 RemoteAPI OpenAPI 规范文件

bob.yuxinyang il y a 1 mois
Parent
commit
d1f2894556
1 fichiers modifiés avec 1026 ajouts et 0 suppressions
  1. 1026 0
      Docs/RemoteAPI.openapi.yaml

+ 1026 - 0
Docs/RemoteAPI.openapi.yaml

@@ -0,0 +1,1026 @@
+openapi: 3.1.0
+info:
+  title: Celestia Trace iOS Remote API
+  version: 2026-07-24
+  description: |
+    iOS 客户端实际调用的远程接口契约。
+
+    维护规则:
+    1. backend 路由、请求字段或响应字段变化时,先更新本文件,再更新 Swift 模型。
+    2. 所有 JSON 接口使用统一响应 `{code, message, data}`。
+    3. 除登录、注册和刷新令牌外,接口均使用 Bearer access token。
+    4. 公网网关保留 `/celestia-trace/v1`;后端 Gin 路由本身使用 `/v1`。
+    5. 服务端时间使用 RFC 3339 / ISO 8601,客户端同时兼容带或不带小数秒。
+  x-client-sources:
+    - CelestiaTrace/Services/Network/APIClient.swift
+    - CelestiaTrace/Services/Network/RemoteNetworkService.swift
+    - CelestiaTrace/Services/Auth/RemoteAuthService.swift
+servers:
+  - url: https://api.ccdw.life/celestia-trace/v1
+    description: Production
+tags:
+  - name: Health
+  - name: Auth
+  - name: User
+  - name: Devices
+  - name: Sessions
+  - name: Assets
+  - name: Sync
+paths:
+  /health:
+    get:
+      tags: [Health]
+      operationId: getHealth
+      security: []
+      responses:
+        "200":
+          description: API and database are healthy
+          content:
+            application/json:
+              schema:
+                $ref: "#/components/schemas/HealthEnvelope"
+        "503":
+          $ref: "#/components/responses/Error"
+
+  /auth/register:
+    post:
+      tags: [Auth]
+      operationId: register
+      security: []
+      requestBody:
+        required: true
+        content:
+          application/json:
+            schema:
+              $ref: "#/components/schemas/RegisterRequest"
+      responses:
+        "200":
+          description: Account and login session created
+          content:
+            application/json:
+              schema:
+                $ref: "#/components/schemas/AuthEnvelope"
+        "400":
+          $ref: "#/components/responses/Error"
+        "409":
+          $ref: "#/components/responses/Error"
+
+  /auth/login:
+    post:
+      tags: [Auth]
+      operationId: login
+      security: []
+      requestBody:
+        required: true
+        content:
+          application/json:
+            schema:
+              $ref: "#/components/schemas/LoginRequest"
+      responses:
+        "200":
+          description: Login session created
+          content:
+            application/json:
+              schema:
+                $ref: "#/components/schemas/AuthEnvelope"
+        "400":
+          $ref: "#/components/responses/Error"
+        "401":
+          $ref: "#/components/responses/Error"
+
+  /auth/refresh:
+    post:
+      tags: [Auth]
+      operationId: refreshAccessToken
+      security: []
+      requestBody:
+        required: true
+        content:
+          application/json:
+            schema:
+              type: object
+              required: [refreshToken]
+              properties:
+                refreshToken:
+                  type: string
+      responses:
+        "200":
+          description: Tokens rotated; the old refresh token is no longer valid
+          content:
+            application/json:
+              schema:
+                $ref: "#/components/schemas/AuthEnvelope"
+        "401":
+          $ref: "#/components/responses/Error"
+
+  /auth/logout:
+    post:
+      tags: [Auth]
+      operationId: logout
+      responses:
+        "200":
+          $ref: "#/components/responses/EmptySuccess"
+        "401":
+          $ref: "#/components/responses/Error"
+
+  /user/profile:
+    get:
+      tags: [User]
+      operationId: getProfile
+      responses:
+        "200":
+          description: Current user
+          content:
+            application/json:
+              schema:
+                $ref: "#/components/schemas/UserEnvelope"
+        "401":
+          $ref: "#/components/responses/Error"
+    put:
+      tags: [User]
+      operationId: updateProfile
+      requestBody:
+        required: true
+        content:
+          application/json:
+            schema:
+              $ref: "#/components/schemas/UpdateProfileRequest"
+      responses:
+        "200":
+          description: Updated user
+          content:
+            application/json:
+              schema:
+                $ref: "#/components/schemas/UserEnvelope"
+        "400":
+          $ref: "#/components/responses/Error"
+        "401":
+          $ref: "#/components/responses/Error"
+        "409":
+          $ref: "#/components/responses/Error"
+
+  /user/change-password:
+    post:
+      tags: [User]
+      operationId: changePassword
+      requestBody:
+        required: true
+        content:
+          application/json:
+            schema:
+              type: object
+              required: [oldPassword, newPassword]
+              properties:
+                oldPassword:
+                  type: string
+                newPassword:
+                  type: string
+                  minLength: 6
+      responses:
+        "200":
+          $ref: "#/components/responses/EmptySuccess"
+        "400":
+          $ref: "#/components/responses/Error"
+        "401":
+          $ref: "#/components/responses/Error"
+
+  /devices:
+    post:
+      tags: [Devices]
+      operationId: bindDevice
+      requestBody:
+        required: true
+        content:
+          application/json:
+            schema:
+              $ref: "#/components/schemas/BindDeviceRequest"
+      responses:
+        "200":
+          description: Bound or updated cloud device
+          content:
+            application/json:
+              schema:
+                $ref: "#/components/schemas/DeviceEnvelope"
+        "400":
+          $ref: "#/components/responses/Error"
+        "401":
+          $ref: "#/components/responses/Error"
+
+  /devices/{deviceId}:
+    parameters:
+      - $ref: "#/components/parameters/DeviceId"
+    put:
+      tags: [Devices]
+      operationId: updateDevice
+      requestBody:
+        required: true
+        content:
+          application/json:
+            schema:
+              $ref: "#/components/schemas/UpdateDeviceRequest"
+      responses:
+        "200":
+          description: Updated cloud device
+          content:
+            application/json:
+              schema:
+                $ref: "#/components/schemas/DeviceEnvelope"
+        "401":
+          $ref: "#/components/responses/Error"
+        "404":
+          $ref: "#/components/responses/Error"
+    delete:
+      tags: [Devices]
+      operationId: unbindDevice
+      responses:
+        "200":
+          description: Device unbound
+          content:
+            application/json:
+              schema:
+                $ref: "#/components/schemas/DeleteEnvelope"
+        "401":
+          $ref: "#/components/responses/Error"
+        "404":
+          $ref: "#/components/responses/Error"
+
+  /sessions:
+    get:
+      tags: [Sessions]
+      operationId: listSessions
+      parameters:
+        - name: includeDeleted
+          in: query
+          schema:
+            type: boolean
+            default: true
+          description: iOS uses true to receive deletion tombstones.
+      responses:
+        "200":
+          description: All sessions visible to the current user
+          content:
+            application/json:
+              schema:
+                $ref: "#/components/schemas/SessionListEnvelope"
+        "401":
+          $ref: "#/components/responses/Error"
+    post:
+      tags: [Sessions]
+      operationId: upsertSession
+      description: |
+        Idempotent upsert by `(userId, clientId)`.
+        Existing sessions must send `baseRevision`; a mismatch returns HTTP/code 409.
+        `deletedEventClientIds` removes events deleted locally since the last pull.
+      requestBody:
+        required: true
+        content:
+          application/json:
+            schema:
+              $ref: "#/components/schemas/SessionUpsertRequest"
+      responses:
+        "200":
+          description: Created or updated session
+          content:
+            application/json:
+              schema:
+                $ref: "#/components/schemas/SessionEnvelope"
+        "400":
+          $ref: "#/components/responses/Error"
+        "401":
+          $ref: "#/components/responses/Error"
+        "409":
+          description: Optimistic-lock conflict
+          content:
+            application/json:
+              schema:
+                $ref: "#/components/schemas/ConflictEnvelope"
+
+  /sessions/{sessionId}/assets:
+    parameters:
+      - $ref: "#/components/parameters/SessionId"
+    post:
+      tags: [Assets]
+      operationId: uploadAsset
+      description: |
+        Used for files smaller than 16 MiB. `clientId` makes retries idempotent.
+        A new upload returns `{asset, sessionRevision}`. An idempotent replay may
+        return the asset directly; the iOS decoder intentionally accepts both.
+      requestBody:
+        required: true
+        content:
+          multipart/form-data:
+            schema:
+              type: object
+              required: [clientId, kind, file]
+              properties:
+                clientId:
+                  type: string
+                kind:
+                  $ref: "#/components/schemas/AssetKind"
+                file:
+                  type: string
+                  format: binary
+      responses:
+        "200":
+          description: Uploaded asset or idempotent existing asset
+          content:
+            application/json:
+              schema:
+                $ref: "#/components/schemas/AssetUploadEnvelope"
+        "401":
+          $ref: "#/components/responses/Error"
+        "404":
+          $ref: "#/components/responses/Error"
+        "413":
+          $ref: "#/components/responses/QuotaError"
+
+  /sessions/{sessionId}/assets/init:
+    parameters:
+      - $ref: "#/components/parameters/SessionId"
+    post:
+      tags: [Assets]
+      operationId: initializeChunkedUpload
+      description: iOS uses this flow for files at least 16 MiB, with 8 MiB chunks.
+      requestBody:
+        required: true
+        content:
+          application/json:
+            schema:
+              $ref: "#/components/schemas/ChunkUploadInitRequest"
+      responses:
+        "200":
+          description: Chunked upload initialized
+          content:
+            application/json:
+              schema:
+                $ref: "#/components/schemas/ChunkUploadInitEnvelope"
+        "401":
+          $ref: "#/components/responses/Error"
+        "404":
+          $ref: "#/components/responses/Error"
+        "413":
+          $ref: "#/components/responses/QuotaError"
+
+  /sessions/{sessionId}/assets/chunk:
+    parameters:
+      - $ref: "#/components/parameters/SessionId"
+    post:
+      tags: [Assets]
+      operationId: uploadChunk
+      requestBody:
+        required: true
+        content:
+          multipart/form-data:
+            schema:
+              type: object
+              required: [uploadId, chunkIndex, file]
+              properties:
+                uploadId:
+                  type: string
+                  format: uuid
+                chunkIndex:
+                  type: integer
+                  minimum: 0
+                file:
+                  type: string
+                  format: binary
+      responses:
+        "200":
+          description: Chunk accepted
+          content:
+            application/json:
+              schema:
+                $ref: "#/components/schemas/ChunkProgressEnvelope"
+        "400":
+          $ref: "#/components/responses/Error"
+        "401":
+          $ref: "#/components/responses/Error"
+        "404":
+          $ref: "#/components/responses/Error"
+        "410":
+          $ref: "#/components/responses/Error"
+
+  /sessions/{sessionId}/assets/complete:
+    parameters:
+      - $ref: "#/components/parameters/SessionId"
+    post:
+      tags: [Assets]
+      operationId: completeChunkedUpload
+      requestBody:
+        required: true
+        content:
+          application/json:
+            schema:
+              type: object
+              required: [uploadId, totalChunks]
+              properties:
+                uploadId:
+                  type: string
+                  format: uuid
+                totalChunks:
+                  type: integer
+                  minimum: 1
+      responses:
+        "200":
+          description: Chunks merged and asset created
+          content:
+            application/json:
+              schema:
+                $ref: "#/components/schemas/AssetUploadEnvelope"
+        "400":
+          $ref: "#/components/responses/Error"
+        "401":
+          $ref: "#/components/responses/Error"
+        "404":
+          $ref: "#/components/responses/Error"
+        "410":
+          $ref: "#/components/responses/Error"
+
+  /sessions/{sessionId}/assets/{assetId}:
+    parameters:
+      - $ref: "#/components/parameters/SessionId"
+      - $ref: "#/components/parameters/AssetId"
+    get:
+      tags: [Assets]
+      operationId: downloadAsset
+      responses:
+        "200":
+          description: Attachment bytes; Content-Disposition contains the original filename
+          content:
+            application/octet-stream:
+              schema:
+                type: string
+                format: binary
+        "401":
+          $ref: "#/components/responses/Error"
+        "404":
+          $ref: "#/components/responses/Error"
+
+  /storage/quota:
+    get:
+      tags: [Sync]
+      operationId: getStorageQuota
+      description: Called before uploading the pending assets for a session.
+      responses:
+        "200":
+          description: Current storage quota in bytes
+          content:
+            application/json:
+              schema:
+                $ref: "#/components/schemas/StorageQuotaEnvelope"
+        "401":
+          $ref: "#/components/responses/Error"
+
+  /sync/trigger:
+    post:
+      tags: [Sync]
+      operationId: recordSyncCheckpoint
+      description: Called only after all session metadata and assets finish syncing.
+      responses:
+        "200":
+          description: Client sync checkpoint recorded
+          content:
+            application/json:
+              schema:
+                $ref: "#/components/schemas/SyncCheckpointEnvelope"
+        "401":
+          $ref: "#/components/responses/Error"
+
+components:
+  securitySchemes:
+    bearerAuth:
+      type: http
+      scheme: bearer
+      bearerFormat: JWT
+  parameters:
+    DeviceId:
+      name: deviceId
+      in: path
+      required: true
+      schema:
+        type: string
+        format: uuid
+    SessionId:
+      name: sessionId
+      in: path
+      required: true
+      schema:
+        type: string
+        format: uuid
+    AssetId:
+      name: assetId
+      in: path
+      required: true
+      schema:
+        type: string
+        format: uuid
+  responses:
+    EmptySuccess:
+      description: Success without a data payload
+      content:
+        application/json:
+          schema:
+            $ref: "#/components/schemas/EmptyEnvelope"
+    Error:
+      description: Request failed
+      content:
+        application/json:
+          schema:
+            $ref: "#/components/schemas/ErrorEnvelope"
+    QuotaError:
+      description: Storage quota exceeded
+      content:
+        application/json:
+          schema:
+            $ref: "#/components/schemas/QuotaErrorEnvelope"
+  schemas:
+    EnvelopeBase:
+      type: object
+      required: [code, message]
+      properties:
+        code:
+          type: integer
+        message:
+          type: string
+    EmptyEnvelope:
+      allOf:
+        - $ref: "#/components/schemas/EnvelopeBase"
+        - type: object
+          properties:
+            data:
+              type: "null"
+    ErrorEnvelope:
+      allOf:
+        - $ref: "#/components/schemas/EnvelopeBase"
+        - type: object
+          properties:
+            data: {}
+    HealthEnvelope:
+      allOf:
+        - $ref: "#/components/schemas/EnvelopeBase"
+        - type: object
+          required: [data]
+          properties:
+            data:
+              type: string
+              const: healthy
+    RegisterRequest:
+      type: object
+      required: [username, identifier, password]
+      properties:
+        username:
+          type: string
+          minLength: 2
+        identifier:
+          type: string
+          description: Email address or phone number.
+        password:
+          type: string
+          minLength: 6
+    LoginRequest:
+      type: object
+      required: [identifier, password]
+      properties:
+        identifier:
+          type: string
+          description: Username, email address, or phone number.
+        password:
+          type: string
+    UpdateProfileRequest:
+      type: object
+      properties:
+        username:
+          type: [string, "null"]
+        email:
+          type: [string, "null"]
+        phoneNumber:
+          type: [string, "null"]
+        avatarURL:
+          type: [string, "null"]
+    User:
+      type: object
+      required: [id, username, registeredAt]
+      properties:
+        id:
+          type: string
+          format: uuid
+        username:
+          type: string
+        email:
+          type: string
+        phoneNumber:
+          type: string
+        avatarURL:
+          type: string
+          format: uri
+        registeredAt:
+          type: string
+          format: date-time
+        updatedAt:
+          type: string
+          format: date-time
+    AuthData:
+      type: object
+      required: [user, token, refreshToken, expiresAt]
+      properties:
+        user:
+          $ref: "#/components/schemas/User"
+        token:
+          type: string
+          description: Short-lived access JWT.
+        refreshToken:
+          type: string
+          description: Rotated on every refresh.
+        expiresAt:
+          type: string
+          format: date-time
+    AuthEnvelope:
+      allOf:
+        - $ref: "#/components/schemas/EnvelopeBase"
+        - type: object
+          required: [data]
+          properties:
+            data:
+              $ref: "#/components/schemas/AuthData"
+    UserEnvelope:
+      allOf:
+        - $ref: "#/components/schemas/EnvelopeBase"
+        - type: object
+          required: [data]
+          properties:
+            data:
+              $ref: "#/components/schemas/User"
+    BindDeviceRequest:
+      type: object
+      required: [name]
+      properties:
+        name:
+          type: string
+        peripheralUUID:
+          type: string
+        hardwareMAC:
+          type: [string, "null"]
+        firmwareVersion:
+          type: [string, "null"]
+        batteryLevel:
+          type: [integer, "null"]
+          minimum: 0
+          maximum: 100
+        freeStorageMB:
+          type: [integer, "null"]
+        totalStorageMB:
+          type: [integer, "null"]
+    UpdateDeviceRequest:
+      type: object
+      properties:
+        name:
+          type: [string, "null"]
+        batteryLevel:
+          type: [integer, "null"]
+          minimum: 0
+          maximum: 100
+        isConnected:
+          type: [boolean, "null"]
+        firmwareVersion:
+          type: [string, "null"]
+        freeStorageMB:
+          type: [integer, "null"]
+        totalStorageMB:
+          type: [integer, "null"]
+    Device:
+      type: object
+      required: [id, name, peripheralUUID]
+      properties:
+        id:
+          type: string
+          format: uuid
+        userId:
+          type: string
+          format: uuid
+        name:
+          type: string
+        peripheralUUID:
+          type: string
+        hardwareMAC:
+          type: string
+        batteryLevel:
+          type: integer
+        firmwareVersion:
+          type: string
+        freeStorageMB:
+          type: integer
+        totalStorageMB:
+          type: integer
+        isConnected:
+          type: boolean
+        boundAt:
+          type: string
+          format: date-time
+        updatedAt:
+          type: string
+          format: date-time
+    DeviceEnvelope:
+      allOf:
+        - $ref: "#/components/schemas/EnvelopeBase"
+        - type: object
+          required: [data]
+          properties:
+            data:
+              $ref: "#/components/schemas/Device"
+    EventUpsert:
+      type: object
+      required: [clientId, relativeTimeMs, eventType]
+      properties:
+        clientId:
+          type: string
+        relativeTimeMs:
+          type: integer
+          format: int64
+        eventType:
+          type: string
+          enum: [PHOTO, NOTE, MARKER, VOICE, CONTINUATION]
+        textContent:
+          type: [string, "null"]
+        voiceStartOffsetMs:
+          type: [integer, "null"]
+          format: int64
+        voiceEndOffsetMs:
+          type: [integer, "null"]
+          format: int64
+    SessionUpsertRequest:
+      type: object
+      required: [clientId, title, startTime, durationMs, events, deletedEventClientIds]
+      properties:
+        clientId:
+          type: string
+        title:
+          type: string
+        startTime:
+          type: string
+          format: date-time
+        endTime:
+          type: [string, "null"]
+          format: date-time
+        durationMs:
+          type: integer
+          format: int64
+        events:
+          type: array
+          items:
+            $ref: "#/components/schemas/EventUpsert"
+        baseRevision:
+          type: [integer, "null"]
+          format: int64
+        deletedEventClientIds:
+          type: array
+          items:
+            type: string
+    RemoteEvent:
+      allOf:
+        - $ref: "#/components/schemas/EventUpsert"
+        - type: object
+          required: [id]
+          properties:
+            id:
+              type: string
+              format: uuid
+            clientId:
+              type: [string, "null"]
+            createdAt:
+              type: string
+              format: date-time
+    RemoteSession:
+      type: object
+      required: [id, title, startTime, durationMs, revision]
+      properties:
+        id:
+          type: string
+          format: uuid
+        clientId:
+          type: [string, "null"]
+        title:
+          type: string
+        startTime:
+          type: string
+          format: date-time
+        endTime:
+          type: [string, "null"]
+          format: date-time
+        durationMs:
+          type: integer
+          format: int64
+        revision:
+          type: integer
+          format: int64
+        deletedAt:
+          type: [string, "null"]
+          format: date-time
+        events:
+          type: array
+          items:
+            $ref: "#/components/schemas/RemoteEvent"
+        assets:
+          type: array
+          items:
+            $ref: "#/components/schemas/Asset"
+    SessionEnvelope:
+      allOf:
+        - $ref: "#/components/schemas/EnvelopeBase"
+        - type: object
+          required: [data]
+          properties:
+            data:
+              $ref: "#/components/schemas/RemoteSession"
+    SessionListEnvelope:
+      allOf:
+        - $ref: "#/components/schemas/EnvelopeBase"
+        - type: object
+          required: [data]
+          properties:
+            data:
+              type: array
+              items:
+                $ref: "#/components/schemas/RemoteSession"
+    ConflictEnvelope:
+      allOf:
+        - $ref: "#/components/schemas/EnvelopeBase"
+        - type: object
+          required: [data]
+          properties:
+            data:
+              type: object
+              required: [serverRevision]
+              properties:
+                serverRevision:
+                  type: integer
+                  format: int64
+    AssetKind:
+      type: string
+      enum: [AUDIO, PHOTO]
+    Asset:
+      type: object
+      required: [id, clientId, kind, fileName, mimeType, sizeBytes, sha256]
+      properties:
+        id:
+          type: string
+          format: uuid
+        clientId:
+          type: string
+        kind:
+          $ref: "#/components/schemas/AssetKind"
+        fileName:
+          type: string
+        mimeType:
+          type: string
+        sizeBytes:
+          type: integer
+          format: int64
+        sha256:
+          type: string
+          pattern: "^[a-fA-F0-9]{64}$"
+        createdAt:
+          type: string
+          format: date-time
+    AssetUploadData:
+      type: object
+      required: [asset, sessionRevision]
+      properties:
+        asset:
+          $ref: "#/components/schemas/Asset"
+        sessionRevision:
+          type: integer
+          format: int64
+    AssetUploadEnvelope:
+      allOf:
+        - $ref: "#/components/schemas/EnvelopeBase"
+        - type: object
+          required: [data]
+          properties:
+            data:
+              oneOf:
+                - $ref: "#/components/schemas/AssetUploadData"
+                - $ref: "#/components/schemas/Asset"
+    ChunkUploadInitRequest:
+      type: object
+      required: [clientId, kind, fileName, mimeType, fileSize, chunkSize]
+      properties:
+        clientId:
+          type: string
+        kind:
+          $ref: "#/components/schemas/AssetKind"
+        fileName:
+          type: string
+        mimeType:
+          type: string
+        fileSize:
+          type: integer
+          format: int64
+          minimum: 1
+        chunkSize:
+          type: integer
+          minimum: 1
+    ChunkUploadInitEnvelope:
+      allOf:
+        - $ref: "#/components/schemas/EnvelopeBase"
+        - type: object
+          required: [data]
+          properties:
+            data:
+              type: object
+              required: [uploadId, totalChunks, chunkSize, expiresAt]
+              properties:
+                uploadId:
+                  type: string
+                  format: uuid
+                totalChunks:
+                  type: integer
+                chunkSize:
+                  type: integer
+                expiresAt:
+                  type: string
+                  format: date-time
+    ChunkProgressEnvelope:
+      allOf:
+        - $ref: "#/components/schemas/EnvelopeBase"
+        - type: object
+          required: [data]
+          properties:
+            data:
+              type: object
+              required: [uploadId, chunkIndex, uploadedChunks, totalChunks]
+              properties:
+                uploadId:
+                  type: string
+                  format: uuid
+                chunkIndex:
+                  type: integer
+                uploadedChunks:
+                  type: integer
+                totalChunks:
+                  type: integer
+    StorageQuota:
+      type: object
+      required: [totalBytes, usedBytes, remainingBytes]
+      properties:
+        totalBytes:
+          type: integer
+          format: int64
+        usedBytes:
+          type: integer
+          format: int64
+        remainingBytes:
+          type: integer
+          format: int64
+    StorageQuotaEnvelope:
+      allOf:
+        - $ref: "#/components/schemas/EnvelopeBase"
+        - type: object
+          required: [data]
+          properties:
+            data:
+              $ref: "#/components/schemas/StorageQuota"
+    QuotaErrorEnvelope:
+      allOf:
+        - $ref: "#/components/schemas/EnvelopeBase"
+        - type: object
+          required: [data]
+          properties:
+            data:
+              allOf:
+                - $ref: "#/components/schemas/StorageQuota"
+                - type: object
+                  required: [requiredBytes]
+                  properties:
+                    requiredBytes:
+                      type: integer
+                      format: int64
+    SyncCheckpointEnvelope:
+      allOf:
+        - $ref: "#/components/schemas/EnvelopeBase"
+        - type: object
+          required: [data]
+          properties:
+            data:
+              type: object
+              required: [syncedAt, message]
+              properties:
+                syncedAt:
+                  type: string
+                  format: date-time
+                message:
+                  type: string
+    DeleteEnvelope:
+      allOf:
+        - $ref: "#/components/schemas/EnvelopeBase"
+        - type: object
+          required: [data]
+          properties:
+            data:
+              type: object
+              required: [deletedId]
+              properties:
+                deletedId:
+                  type: string
+
+security:
+  - bearerAuth: []