Files
NianToB/docs/SERVER_CONTRACT_V0.md

4.7 KiB

YINIAN Server Contract v0

Updated: 2026-04-26
Status: Draft contract for M2 integration
Client switch: set YINIAN_API_BASE_URL to enable HTTP mode. Without it, desktop uses mock mode.

1. Transport

  • Base URL: YINIAN_API_BASE_URL
  • Content type: application/json
  • Auth header: Authorization: Bearer <accessToken>
  • Desktop header: X-YINIAN-App-Version: 0.1.0
  • Timestamps: Unix milliseconds.
  • Field casing: server should prefer camelCase; desktop accepts key snake_case aliases for M1/M2 migration.

2. Error Shape

Non-2xx responses should return:

{
  "error": {
    "code": "SESSION_EXPIRED",
    "message": "Session expired"
  }
}

The desktop currently surfaces error.message and falls back to YINIAN API request failed: <status>.

Recommended error codes:

  • INVALID_CREDENTIALS
  • INVALID_SMS_CODE
  • SESSION_EXPIRED
  • WORKSPACE_FORBIDDEN
  • CONFIG_UNAVAILABLE
  • SKILLS_MANIFEST_UNAVAILABLE

3. Device Payload

Login requests include:

{
  "device": {
    "device_id": "dev_local_electron",
    "platform": "darwin",
    "app_version": "0.1.0",
    "machine_name": "YINIAN Desktop"
  }
}

The exact device_id is a placeholder in M2 and should be replaced by a stable desktop installation id later.

4. Auth

POST /auth/login/sms

Request:

{
  "phone": "13800000000",
  "code": "123456",
  "device": {}
}

Response:

{
  "accessToken": "access_demo",
  "refreshToken": "refresh_demo",
  "accessTokenExpiresAt": 1777188600000
}

POST /auth/login/password

Request:

{
  "account": "ops@example.com",
  "password": "secret",
  "device": {}
}

Response shape is the same as SMS login.

POST /auth/refresh

Request:

{
  "refreshToken": "refresh_demo",
  "device": {}
}

Response:

{
  "accessToken": "access_refreshed",
  "refreshToken": "refresh_rotated",
  "accessTokenExpiresAt": 1777189500000
}

M2 desktop persists only refreshToken; accessToken remains memory-only.

GET /auth/me

Headers:

Authorization: Bearer access_demo

Response:

{
  "user": {
    "id": "user_ops_001",
    "name": "王管理员",
    "phone": "13800000000",
    "email": "ops@example.com",
    "avatar": "https://cdn.example.com/avatar.png"
  },
  "hotels": [
    {
      "id": "workspace_hangzhou_ops",
      "name": "智念企业组织空间",
      "brand": "智念"
    }
  ],
  "currentHotelId": "workspace_hangzhou_ops",
  "accessTokenExpiresAt": 1777188600000
}

5. Config Sync

GET /config/sync?hotel_id=<hotelId>

Response:

{
  "serverTime": 1777188000000,
  "hotel": {
    "id": "workspace_hangzhou_ops",
    "name": "智念企业组织空间",
    "brand": "智念"
  },
  "entitlements": [
    {
      "skillId": "daily-report",
      "name": "日报生成助手",
      "version": "1.0.0",
      "enabled": true,
      "category": "reporting",
      "triggers": ["scheduled", "manual"],
      "lastRunAt": 1777184400000
    }
  ],
  "notificationChannels": [
    {
      "id": "wechat_ops",
      "kind": "wecom",
      "label": "业务通知群",
      "recipient": "room_001",
      "enabled": true,
      "source": "nianxx"
    }
  ],
  "featureFlags": {
    "skillsSync": true,
    "advancedSettings": false
  },
  "uiPolicy": {
    "defaultPage": "today",
    "showAdvancedSettings": false
  }
}

6. Skills Manifest

GET /skills/manifest?hotel_id=<hotelId>

Response:

{
  "serverTime": 1777188000000,
  "hotelId": "workspace_hangzhou_ops",
  "manifestVersion": "2026.04.26.1",
  "skills": [
    {
      "skillId": "data-check",
      "name": "数据检查助手",
      "version": "1.2.0",
      "enabled": true,
      "bundleSha256": "sha256-demo",
      "bundleUrl": "https://cdn.example.com/skills/data-check-1.2.0.tgz"
    },
    {
      "skillId": "customer-reply-helper",
      "name": "客户回复助手",
      "version": "0.9.0",
      "enabled": false,
      "bundleSha256": "sha256-disabled"
    }
  ]
}

M2 desktop consumes manifest metadata only. It does not download bundleUrl, verify signatures, or unpack bundles yet.

7. Client Normalization Rules

The desktop accepts:

  • camelCase and snake_case aliases for known fields.
  • Missing user.name as 未命名用户.
  • Missing hotel fields with safe empty/default values.
  • Unknown skill categories as ops-automation.
  • Unknown trigger values are ignored.
  • Missing uiPolicy.defaultPage defaults to today.
  • Missing arrays default to [].

Malformed server responses should not break the shell, but required auth fields still fail fast:

  • Missing accessToken after login or refresh throws 服务端未返回 access token.
  • Authenticated config/skills calls without a session throw 请先登录.