@serviceai/api-spec 1.1.23 → 1.2.1

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (4) hide show
  1. package/openapi.json +1325 -107
  2. package/openapi.yaml +806 -34
  3. package/package.json +1 -1
  4. package/types.d.ts +678 -13
package/openapi.yaml CHANGED
@@ -1,7 +1,7 @@
1
1
  openapi: 3.1.0
2
2
  info:
3
3
  title: ServiceAi API
4
- version: 1.1.23
4
+ version: 1.2.1
5
5
  description: >-
6
6
  Source-of-truth API contract for ServiceAi. Generated from `shared/schema.ts` Zod schemas by
7
7
  `scripts/build-api-spec.ts`. Versioning policy: see `docs/api-versioning.md`. The hand-written docs/ios-contract.md
@@ -78,7 +78,7 @@ components:
78
78
  properties:
79
79
  apiVersion:
80
80
  type: string
81
- example: 1.1.23
81
+ example: 1.2.1
82
82
  minClientVersion:
83
83
  type: string
84
84
  example: 1.0.0
@@ -1234,9 +1234,49 @@ components:
1234
1234
  type: string
1235
1235
  category:
1236
1236
  type: string
1237
+ roomLocalId:
1238
+ type: string
1239
+ maxLength: 128
1237
1240
  walls:
1238
1241
  type: array
1239
- items: {}
1242
+ items:
1243
+ type: object
1244
+ properties:
1245
+ start:
1246
+ type: object
1247
+ properties:
1248
+ x:
1249
+ type: number
1250
+ 'y':
1251
+ type: number
1252
+ required:
1253
+ - x
1254
+ - 'y'
1255
+ end:
1256
+ type: object
1257
+ properties:
1258
+ x:
1259
+ type: number
1260
+ 'y':
1261
+ type: number
1262
+ required:
1263
+ - x
1264
+ - 'y'
1265
+ length:
1266
+ type: number
1267
+ height:
1268
+ type: number
1269
+ captureMetadata:
1270
+ type: object
1271
+ properties:
1272
+ identifier:
1273
+ type: string
1274
+ isDoor:
1275
+ type: boolean
1276
+ isOpening:
1277
+ type: boolean
1278
+ captureMethod:
1279
+ type: string
1240
1280
  doors:
1241
1281
  type: array
1242
1282
  items:
@@ -1249,12 +1289,9 @@ components:
1249
1289
  type: number
1250
1290
  'y':
1251
1291
  type: number
1252
- z:
1253
- type: number
1254
1292
  required:
1255
1293
  - x
1256
1294
  - 'y'
1257
- - z
1258
1295
  width:
1259
1296
  type: number
1260
1297
  exclusiveMinimum: 0
@@ -1296,12 +1333,9 @@ components:
1296
1333
  type: number
1297
1334
  'y':
1298
1335
  type: number
1299
- z:
1300
- type: number
1301
1336
  required:
1302
1337
  - x
1303
1338
  - 'y'
1304
- - z
1305
1339
  width:
1306
1340
  type: number
1307
1341
  exclusiveMinimum: 0
@@ -1338,12 +1372,9 @@ components:
1338
1372
  type: number
1339
1373
  'y':
1340
1374
  type: number
1341
- z:
1342
- type: number
1343
1375
  required:
1344
1376
  - x
1345
1377
  - 'y'
1346
- - z
1347
1378
  width:
1348
1379
  type: number
1349
1380
  exclusiveMinimum: 0
@@ -1368,6 +1399,83 @@ components:
1368
1399
  - position
1369
1400
  - width
1370
1401
  - height
1402
+ pins:
1403
+ type: array
1404
+ items:
1405
+ type: object
1406
+ properties:
1407
+ id:
1408
+ type: string
1409
+ minLength: 1
1410
+ maxLength: 128
1411
+ position:
1412
+ type: object
1413
+ properties:
1414
+ x:
1415
+ type: number
1416
+ 'y':
1417
+ type: number
1418
+ required:
1419
+ - x
1420
+ - 'y'
1421
+ label:
1422
+ type: string
1423
+ maxLength: 256
1424
+ roomLocalId:
1425
+ type: string
1426
+ maxLength: 128
1427
+ kind:
1428
+ type: string
1429
+ maxLength: 64
1430
+ note:
1431
+ type: string
1432
+ maxLength: 1024
1433
+ required:
1434
+ - id
1435
+ - position
1436
+ objects:
1437
+ type: array
1438
+ items:
1439
+ type: object
1440
+ properties:
1441
+ category:
1442
+ type: string
1443
+ position:
1444
+ type: object
1445
+ properties:
1446
+ x:
1447
+ type: number
1448
+ 'y':
1449
+ type: number
1450
+ required:
1451
+ - x
1452
+ - 'y'
1453
+ dimensions:
1454
+ type: object
1455
+ properties:
1456
+ width:
1457
+ type: number
1458
+ height:
1459
+ type: number
1460
+ depth:
1461
+ type: number
1462
+ boundaryPolygon:
1463
+ type: array
1464
+ items:
1465
+ type: object
1466
+ properties:
1467
+ x:
1468
+ type: number
1469
+ 'y':
1470
+ type: number
1471
+ required:
1472
+ - x
1473
+ - 'y'
1474
+ roomNote:
1475
+ type: string
1476
+ maxLength: 4096
1477
+ entryDoorwayId:
1478
+ type: string
1371
1479
  description: >-
1372
1480
  Mobile-upload scan room. `doors`, `windows`, and `openings` are three distinct non-overlapping arrays
1373
1481
  — array membership IS the discriminator (there is no `kind` / `type` field on the item itself).
@@ -1377,6 +1485,11 @@ components:
1377
1485
  an `entryDoorwayId` target.
1378
1486
  required:
1379
1487
  - rooms
1488
+ description: >-
1489
+ RoomPlan JSON envelope. `rooms[]` carries the per-room geometry
1490
+ (walls/doors/windows/openings/pins/objects/boundaryPolygon). iOS continues to upload positions in the
1491
+ RoomPlan-native frame; the server applies the view-alignment intake transform once at `/scans/mobile-upload`
1492
+ (see docs/lidar-contract-v4.md).
1380
1493
  name:
1381
1494
  type: string
1382
1495
  minLength: 1
@@ -1391,8 +1504,27 @@ components:
1391
1504
  userOrientationHint:
1392
1505
  type: string
1393
1506
  wallConfidences:
1394
- type: array
1395
- items: {}
1507
+ anyOf:
1508
+ - type: array
1509
+ items:
1510
+ type:
1511
+ - array
1512
+ - 'null'
1513
+ items:
1514
+ type: number
1515
+ - type: array
1516
+ items:
1517
+ type: number
1518
+ - type: object
1519
+ additionalProperties:
1520
+ type: array
1521
+ items:
1522
+ type: number
1523
+ description: >-
1524
+ Per-room per-wall capture confidences. Canonical shape: nested `(number[] | null)[]` parallel to
1525
+ `roomPlanJSON.rooms[]`. Two legacy shapes are still accepted for backwards compatibility but should not be
1526
+ emitted by new clients: a flat `number[]` (treated as room[0] confidences) and an object keyed by
1527
+ `roomLocalId` (or numeric index as string). See `normalizeWallConfidences` in `server/routes/lidar.ts`.
1396
1528
  deviceTrack: {}
1397
1529
  referencePhotos:
1398
1530
  type: array
@@ -1473,12 +1605,42 @@ components:
1473
1605
  type: string
1474
1606
  clientMirrorApplied:
1475
1607
  type: boolean
1608
+ parentScanId:
1609
+ type: integer
1610
+ exclusiveMinimum: 0
1611
+ entryDoorwayId:
1612
+ type: string
1613
+ description: >-
1614
+ Append-room doorway link to a target room on the parent composite. Format:
1615
+ `scan-<scanId>-room-<roomLocalId>-(door|opening)-<idx>`. The trailing segment selects kind-specifically —
1616
+ `door` indexes the target room's `doors[]`, `opening` indexes `openings[]`; the two are never folded.
1617
+ `<scanId>` and `<roomLocalId>` must reference a scan + room on the parent composite the iOS client is
1618
+ appending to.
1619
+ entryDoorwayWallIndex:
1620
+ type: integer
1621
+ minimum: 0
1622
+ entryDoorwayPosition:
1623
+ type: object
1624
+ properties:
1625
+ x:
1626
+ type: number
1627
+ 'y':
1628
+ type: number
1629
+ required:
1630
+ - x
1631
+ - 'y'
1476
1632
  required:
1477
1633
  - roomPlanJSON
1478
1634
  - name
1479
1635
  description: >-
1480
- POST /api/jobs/:jobId/scans/mobile-upload body. Required: `roomPlanJSON` (object with nested `rooms` array) +
1481
- `name`. All other fields are additive / optional per the v2 contract.
1636
+ POST /api/jobs/:jobId/scans/mobile-upload body. v5 of the LiDAR contract (Task #879) — additive over v3/v4.
1637
+ Required: `roomPlanJSON` (object with nested `rooms` array) + `name`. All v3 and v4 payloads remain accepted
1638
+ byte-for-byte. New first-class fields: per-room `roomLocalId`, `pins[]`, `objects[]`, `boundaryPolygon[]`,
1639
+ per-wall `captureMetadata`; append-room `parentScanId`, `entryDoorwayId`, `entryDoorwayWallIndex`,
1640
+ `entryDoorwayPosition`; opening `position` is 2D `{x, y}` (an additional `z` is tolerated for backwards
1641
+ compatibility but ignored by the server). `wallConfidences` advertises the canonical nested `(number[] |
1642
+ null)[]` shape; the two legacy shapes (flat `number[]` and `Record<roomLocalIdOrIndex, number[]>`) are still
1643
+ parsed by `normalizeWallConfidences` in `server/routes/lidar.ts`.
1482
1644
  MobileUploadResponse:
1483
1645
  type: object
1484
1646
  properties:
@@ -1693,6 +1855,169 @@ components:
1693
1855
  description: >-
1694
1856
  Persisted floor-plan PNG render-frame. `mirrorX` is derived from `scans.client_mirror_applied` (negate when
1695
1857
  false). Null sentinel: ANY required column NULL → `null` is emitted instead of a partial frame.
1858
+ ChattyToolDefinition:
1859
+ type: object
1860
+ properties:
1861
+ type:
1862
+ type: string
1863
+ enum:
1864
+ - function
1865
+ function:
1866
+ type: object
1867
+ properties:
1868
+ name:
1869
+ type: string
1870
+ minLength: 1
1871
+ description:
1872
+ type: string
1873
+ parameters:
1874
+ type: object
1875
+ properties:
1876
+ type:
1877
+ type: string
1878
+ enum:
1879
+ - object
1880
+ properties:
1881
+ type: object
1882
+ additionalProperties:
1883
+ allOf:
1884
+ - type: object
1885
+ additionalProperties: {}
1886
+ - type: object
1887
+ properties:
1888
+ type:
1889
+ anyOf:
1890
+ - type: string
1891
+ - type: array
1892
+ items:
1893
+ type: string
1894
+ description:
1895
+ type: string
1896
+ enum:
1897
+ type: array
1898
+ items:
1899
+ type: string
1900
+ required:
1901
+ type: array
1902
+ items:
1903
+ type: string
1904
+ additionalProperties:
1905
+ type: boolean
1906
+ required:
1907
+ - type
1908
+ required:
1909
+ type: array
1910
+ items:
1911
+ type: string
1912
+ additionalProperties:
1913
+ type: boolean
1914
+ required:
1915
+ - type
1916
+ - properties
1917
+ required:
1918
+ - name
1919
+ - description
1920
+ - parameters
1921
+ required:
1922
+ - type
1923
+ - function
1924
+ ChattyExecuteRequest:
1925
+ type: object
1926
+ properties:
1927
+ function:
1928
+ type: string
1929
+ minLength: 1
1930
+ name:
1931
+ type: string
1932
+ minLength: 1
1933
+ arguments:
1934
+ type: object
1935
+ additionalProperties: {}
1936
+ ChattyExecuteResponse:
1937
+ type: object
1938
+ properties:
1939
+ success:
1940
+ type: boolean
1941
+ message:
1942
+ type: string
1943
+ error:
1944
+ type: string
1945
+ code:
1946
+ type: string
1947
+ data: {}
1948
+ action: {}
1949
+ required:
1950
+ - success
1951
+ ChattyAiContextResponse:
1952
+ type: object
1953
+ properties:
1954
+ instructions:
1955
+ type: string
1956
+ knowledgeBaseLoaded:
1957
+ type: boolean
1958
+ workflowStagesLoaded:
1959
+ type: boolean
1960
+ timestamp:
1961
+ type: string
1962
+ required:
1963
+ - instructions
1964
+ - knowledgeBaseLoaded
1965
+ - workflowStagesLoaded
1966
+ - timestamp
1967
+ RealtimeTokenRequest:
1968
+ type: object
1969
+ properties:
1970
+ directWebRTC:
1971
+ type: boolean
1972
+ RealtimeTokenResponse:
1973
+ type: object
1974
+ properties:
1975
+ sessionId:
1976
+ type: string
1977
+ model:
1978
+ type: string
1979
+ voice:
1980
+ type: string
1981
+ clientSecret:
1982
+ type: string
1983
+ fallback:
1984
+ type: boolean
1985
+ directWebRTC:
1986
+ type: boolean
1987
+ useWebRTC:
1988
+ type: boolean
1989
+ expiresInSeconds:
1990
+ type: integer
1991
+ minimum: 0
1992
+ metadata:
1993
+ type: object
1994
+ properties:
1995
+ organizationId:
1996
+ anyOf:
1997
+ - type: string
1998
+ - type: number
1999
+ required:
2000
+ - organizationId
2001
+ message:
2002
+ type: string
2003
+ required:
2004
+ - fallback
2005
+ CsrfTokenResponse:
2006
+ type: object
2007
+ properties:
2008
+ token:
2009
+ type: string
2010
+ minLength: 1
2011
+ required:
2012
+ - token
2013
+ RealtimeWebrtcRequest:
2014
+ type: object
2015
+ properties:
2016
+ sdp:
2017
+ type: string
2018
+ minLength: 1
2019
+ required:
2020
+ - sdp
1696
2021
  parameters: {}
1697
2022
  paths:
1698
2023
  /api/version:
@@ -2831,12 +3156,9 @@ paths:
2831
3156
  type: number
2832
3157
  'y':
2833
3158
  type: number
2834
- z:
2835
- type: number
2836
3159
  required:
2837
3160
  - x
2838
3161
  - 'y'
2839
- - z
2840
3162
  width:
2841
3163
  type: number
2842
3164
  exclusiveMinimum: 0
@@ -2876,12 +3198,9 @@ paths:
2876
3198
  type: number
2877
3199
  'y':
2878
3200
  type: number
2879
- z:
2880
- type: number
2881
3201
  required:
2882
3202
  - x
2883
3203
  - 'y'
2884
- - z
2885
3204
  width:
2886
3205
  type: number
2887
3206
  exclusiveMinimum: 0
@@ -2921,12 +3240,9 @@ paths:
2921
3240
  type: number
2922
3241
  'y':
2923
3242
  type: number
2924
- z:
2925
- type: number
2926
3243
  required:
2927
3244
  - x
2928
3245
  - 'y'
2929
- - z
2930
3246
  width:
2931
3247
  type: number
2932
3248
  exclusiveMinimum: 0
@@ -3081,12 +3397,9 @@ paths:
3081
3397
  type: number
3082
3398
  'y':
3083
3399
  type: number
3084
- z:
3085
- type: number
3086
3400
  required:
3087
3401
  - x
3088
3402
  - 'y'
3089
- - z
3090
3403
  width:
3091
3404
  type: number
3092
3405
  exclusiveMinimum: 0
@@ -3126,12 +3439,9 @@ paths:
3126
3439
  type: number
3127
3440
  'y':
3128
3441
  type: number
3129
- z:
3130
- type: number
3131
3442
  required:
3132
3443
  - x
3133
3444
  - 'y'
3134
- - z
3135
3445
  width:
3136
3446
  type: number
3137
3447
  exclusiveMinimum: 0
@@ -3171,12 +3481,9 @@ paths:
3171
3481
  type: number
3172
3482
  'y':
3173
3483
  type: number
3174
- z:
3175
- type: number
3176
3484
  required:
3177
3485
  - x
3178
3486
  - 'y'
3179
- - z
3180
3487
  width:
3181
3488
  type: number
3182
3489
  exclusiveMinimum: 0
@@ -3243,4 +3550,469 @@ paths:
3243
3550
  application/json:
3244
3551
  schema:
3245
3552
  $ref: '#/components/schemas/ErrorEnvelope'
3553
+ /api/chatty-voice/tools:
3554
+ get:
3555
+ summary: List the OpenAI-Realtime-compatible function tools the executor can run.
3556
+ description: >-
3557
+ Returns the canonical Chatty Voice tool catalogue as a BARE ARRAY of `ChattyToolDefinition` envelopes (no
3558
+ wrapping object). Each entry is executable via POST /api/chatty-voice/tools/execute.
3559
+ tags:
3560
+ - chatty
3561
+ parameters:
3562
+ - *ref_2
3563
+ responses:
3564
+ '200':
3565
+ description: Bare array of tool definitions.
3566
+ content:
3567
+ application/json:
3568
+ schema:
3569
+ type: array
3570
+ items:
3571
+ type: object
3572
+ properties:
3573
+ type:
3574
+ type: string
3575
+ enum:
3576
+ - function
3577
+ function:
3578
+ type: object
3579
+ properties:
3580
+ name:
3581
+ type: string
3582
+ minLength: 1
3583
+ description:
3584
+ type: string
3585
+ parameters:
3586
+ type: object
3587
+ properties:
3588
+ type:
3589
+ type: string
3590
+ enum:
3591
+ - object
3592
+ properties:
3593
+ type: object
3594
+ additionalProperties:
3595
+ allOf:
3596
+ - type: object
3597
+ additionalProperties: {}
3598
+ - type: object
3599
+ properties:
3600
+ type:
3601
+ anyOf:
3602
+ - type: string
3603
+ - type: array
3604
+ items:
3605
+ type: string
3606
+ description:
3607
+ type: string
3608
+ enum:
3609
+ type: array
3610
+ items:
3611
+ type: string
3612
+ required:
3613
+ type: array
3614
+ items:
3615
+ type: string
3616
+ additionalProperties:
3617
+ type: boolean
3618
+ required:
3619
+ - type
3620
+ required:
3621
+ type: array
3622
+ items:
3623
+ type: string
3624
+ additionalProperties:
3625
+ type: boolean
3626
+ required:
3627
+ - type
3628
+ - properties
3629
+ required:
3630
+ - name
3631
+ - description
3632
+ - parameters
3633
+ required:
3634
+ - type
3635
+ - function
3636
+ '401':
3637
+ description: Unauthenticated.
3638
+ content:
3639
+ application/json:
3640
+ schema:
3641
+ $ref: '#/components/schemas/ErrorEnvelope'
3642
+ '403':
3643
+ description: Caller lacks the chatty-voice entitlement.
3644
+ content:
3645
+ application/json:
3646
+ schema:
3647
+ $ref: '#/components/schemas/ErrorEnvelope'
3648
+ '426':
3649
+ description: Client major below minClientVersion.
3650
+ content:
3651
+ application/json:
3652
+ schema:
3653
+ $ref: '#/components/schemas/UpgradeRequiredEnvelope'
3654
+ /api/chatty-voice/tools/execute:
3655
+ post:
3656
+ summary: Execute a Chatty Voice tool by canonical `function` (or legacy `name`).
3657
+ description: >-
3658
+ Body accepts either the canonical `function` field or the legacy `name` alias — at least one is required. The
3659
+ executor treats them as equivalent and dispatches to the same switch case in `tools.ts`. Tool arguments are
3660
+ passed through verbatim as an opaque JSON object.
3661
+ tags:
3662
+ - chatty
3663
+ parameters:
3664
+ - *ref_2
3665
+ requestBody:
3666
+ content:
3667
+ application/json:
3668
+ schema:
3669
+ type: object
3670
+ properties:
3671
+ function:
3672
+ type: string
3673
+ minLength: 1
3674
+ name:
3675
+ type: string
3676
+ minLength: 1
3677
+ arguments:
3678
+ type: object
3679
+ additionalProperties: {}
3680
+ responses:
3681
+ '200':
3682
+ description: >-
3683
+ Always returned when execution reaches the executor. `success: false` envelopes carry an `error` and (for
3684
+ permission denials) a `code` — including the missing/unknown function case.
3685
+ content:
3686
+ application/json:
3687
+ schema:
3688
+ type: object
3689
+ properties:
3690
+ success:
3691
+ type: boolean
3692
+ message:
3693
+ type: string
3694
+ error:
3695
+ type: string
3696
+ code:
3697
+ type: string
3698
+ data: {}
3699
+ action: {}
3700
+ required:
3701
+ - success
3702
+ '401':
3703
+ description: Unauthenticated.
3704
+ content:
3705
+ application/json:
3706
+ schema:
3707
+ $ref: '#/components/schemas/ErrorEnvelope'
3708
+ '403':
3709
+ description: Caller lacks the chatty-voice entitlement or a per-tool permission.
3710
+ content:
3711
+ application/json:
3712
+ schema:
3713
+ $ref: '#/components/schemas/ErrorEnvelope'
3714
+ '500':
3715
+ description: 'Unhandled executor error. Body is `{ success: false, error }` (same envelope as 200).'
3716
+ content:
3717
+ application/json:
3718
+ schema:
3719
+ type: object
3720
+ properties:
3721
+ success:
3722
+ type: boolean
3723
+ message:
3724
+ type: string
3725
+ error:
3726
+ type: string
3727
+ code:
3728
+ type: string
3729
+ data: {}
3730
+ action: {}
3731
+ required:
3732
+ - success
3733
+ /api/chatty-voice/ai-context:
3734
+ get:
3735
+ summary: Fetch the Chatty Voice system prompt + load-status flags.
3736
+ tags:
3737
+ - chatty
3738
+ parameters:
3739
+ - *ref_2
3740
+ responses:
3741
+ '200':
3742
+ description: System instructions + knowledge-base / workflow-stage load flags.
3743
+ content:
3744
+ application/json:
3745
+ schema:
3746
+ type: object
3747
+ properties:
3748
+ instructions:
3749
+ type: string
3750
+ knowledgeBaseLoaded:
3751
+ type: boolean
3752
+ workflowStagesLoaded:
3753
+ type: boolean
3754
+ timestamp:
3755
+ type: string
3756
+ required:
3757
+ - instructions
3758
+ - knowledgeBaseLoaded
3759
+ - workflowStagesLoaded
3760
+ - timestamp
3761
+ '401':
3762
+ description: Unauthenticated.
3763
+ content:
3764
+ application/json:
3765
+ schema:
3766
+ $ref: '#/components/schemas/ErrorEnvelope'
3767
+ '403':
3768
+ description: Caller lacks the chatty-voice entitlement.
3769
+ content:
3770
+ application/json:
3771
+ schema:
3772
+ $ref: '#/components/schemas/ErrorEnvelope'
3773
+ /api/realtime-token:
3774
+ post:
3775
+ summary: Mint an OpenAI Realtime ephemeral session token (single union object).
3776
+ description: >-
3777
+ Body: `{ directWebRTC?: boolean }`. Response is a SINGLE union object — the directWebRTC, desktop
3778
+ server-proxied, and HTTP-fallback branches all share the same envelope. Only `fallback` is always present; every
3779
+ other field is optional and presence depends on the branch.
3780
+ tags:
3781
+ - chatty
3782
+ parameters:
3783
+ - *ref_2
3784
+ requestBody:
3785
+ content:
3786
+ application/json:
3787
+ schema:
3788
+ type: object
3789
+ properties:
3790
+ directWebRTC:
3791
+ type: boolean
3792
+ responses:
3793
+ '200':
3794
+ description: Session minted (or fallback signalled). See union shape.
3795
+ content:
3796
+ application/json:
3797
+ schema:
3798
+ type: object
3799
+ properties:
3800
+ sessionId:
3801
+ type: string
3802
+ model:
3803
+ type: string
3804
+ voice:
3805
+ type: string
3806
+ clientSecret:
3807
+ type: string
3808
+ fallback:
3809
+ type: boolean
3810
+ directWebRTC:
3811
+ type: boolean
3812
+ useWebRTC:
3813
+ type: boolean
3814
+ expiresInSeconds:
3815
+ type: integer
3816
+ minimum: 0
3817
+ metadata:
3818
+ type: object
3819
+ properties:
3820
+ organizationId:
3821
+ anyOf:
3822
+ - type: string
3823
+ - type: number
3824
+ required:
3825
+ - organizationId
3826
+ message:
3827
+ type: string
3828
+ required:
3829
+ - fallback
3830
+ '401':
3831
+ description: Unauthenticated.
3832
+ content:
3833
+ application/json:
3834
+ schema:
3835
+ $ref: '#/components/schemas/ErrorEnvelope'
3836
+ '403':
3837
+ description: Caller lacks the chatty-voice entitlement.
3838
+ content:
3839
+ application/json:
3840
+ schema:
3841
+ $ref: '#/components/schemas/ErrorEnvelope'
3842
+ '405':
3843
+ description: Method Not Allowed (POST only).
3844
+ content:
3845
+ application/json:
3846
+ schema:
3847
+ $ref: '#/components/schemas/ErrorEnvelope'
3848
+ '429':
3849
+ description: Per-user rate limit on token mint.
3850
+ content:
3851
+ application/json:
3852
+ schema:
3853
+ $ref: '#/components/schemas/ErrorEnvelope'
3854
+ '500':
3855
+ description: 'Mint failure — body still carries `fallback: true`.'
3856
+ content:
3857
+ application/json:
3858
+ schema:
3859
+ type: object
3860
+ properties:
3861
+ sessionId:
3862
+ type: string
3863
+ model:
3864
+ type: string
3865
+ voice:
3866
+ type: string
3867
+ clientSecret:
3868
+ type: string
3869
+ fallback:
3870
+ type: boolean
3871
+ directWebRTC:
3872
+ type: boolean
3873
+ useWebRTC:
3874
+ type: boolean
3875
+ expiresInSeconds:
3876
+ type: integer
3877
+ minimum: 0
3878
+ metadata:
3879
+ type: object
3880
+ properties:
3881
+ organizationId:
3882
+ anyOf:
3883
+ - type: string
3884
+ - type: number
3885
+ required:
3886
+ - organizationId
3887
+ message:
3888
+ type: string
3889
+ required:
3890
+ - fallback
3891
+ /api/csrf-token:
3892
+ get:
3893
+ summary: Mint a short-lived CSRF token for the WebRTC SDP exchange.
3894
+ description: >-
3895
+ Returned token is required as `X-CSRF-Token` on POST /api/realtime/webrtc and is bound to the calling user for 5
3896
+ minutes.
3897
+ tags:
3898
+ - chatty
3899
+ parameters:
3900
+ - *ref_2
3901
+ responses:
3902
+ '200':
3903
+ description: Fresh CSRF token.
3904
+ content:
3905
+ application/json:
3906
+ schema:
3907
+ type: object
3908
+ properties:
3909
+ token:
3910
+ type: string
3911
+ minLength: 1
3912
+ required:
3913
+ - token
3914
+ '401':
3915
+ description: Unauthenticated.
3916
+ content:
3917
+ application/json:
3918
+ schema:
3919
+ $ref: '#/components/schemas/ErrorEnvelope'
3920
+ '403':
3921
+ description: Caller lacks the chatty-voice entitlement.
3922
+ content:
3923
+ application/json:
3924
+ schema:
3925
+ $ref: '#/components/schemas/ErrorEnvelope'
3926
+ /api/realtime/webrtc:
3927
+ post:
3928
+ summary: Server-proxied OpenAI Realtime SDP exchange (desktop branch).
3929
+ description: >-
3930
+ Request body is JSON `{ sdp }`; the server mints a fresh OpenAI ephemeral client_secret, forwards the raw SDP to
3931
+ OpenAI's WebRTC endpoint, and returns the raw SDP answer as `application/sdp` text. CSRF-protected via
3932
+ `X-CSRF-Token` (see GET /api/csrf-token) and rate-limited to 3 req/min/IP.
3933
+ tags:
3934
+ - chatty
3935
+ parameters:
3936
+ - *ref_2
3937
+ - name: X-CSRF-Token
3938
+ in: header
3939
+ required: true
3940
+ schema:
3941
+ type: string
3942
+ description: CSRF token previously minted by GET /api/csrf-token.
3943
+ requestBody:
3944
+ content:
3945
+ application/json:
3946
+ schema:
3947
+ type: object
3948
+ properties:
3949
+ sdp:
3950
+ type: string
3951
+ minLength: 1
3952
+ required:
3953
+ - sdp
3954
+ responses:
3955
+ '200':
3956
+ description: Raw OpenAI SDP answer (text body, not JSON).
3957
+ content:
3958
+ application/sdp:
3959
+ schema:
3960
+ type: string
3961
+ '400':
3962
+ description: Invalid SDP format.
3963
+ content:
3964
+ application/json:
3965
+ schema:
3966
+ $ref: '#/components/schemas/ErrorEnvelope'
3967
+ '401':
3968
+ description: Unauthenticated.
3969
+ content:
3970
+ application/json:
3971
+ schema:
3972
+ $ref: '#/components/schemas/ErrorEnvelope'
3973
+ '403':
3974
+ description: Caller lacks the chatty-voice entitlement, or CSRF token invalid.
3975
+ content:
3976
+ application/json:
3977
+ schema:
3978
+ $ref: '#/components/schemas/ErrorEnvelope'
3979
+ '429':
3980
+ description: Rate limit (3 req/min/IP).
3981
+ content:
3982
+ application/json:
3983
+ schema:
3984
+ $ref: '#/components/schemas/ErrorEnvelope'
3985
+ '500':
3986
+ description: SDP exchange failed locally.
3987
+ content:
3988
+ application/json:
3989
+ schema:
3990
+ $ref: '#/components/schemas/ErrorEnvelope'
3991
+ '502':
3992
+ description: >-
3993
+ Local failure minting the OpenAI ephemeral client_secret required to authenticate the SDP exchange. Returned
3994
+ as a JSON envelope generated by this server: `{ error, status? }`.
3995
+ content:
3996
+ application/json:
3997
+ schema:
3998
+ type: object
3999
+ properties:
4000
+ error:
4001
+ type: string
4002
+ status:
4003
+ type: integer
4004
+ required:
4005
+ - error
4006
+ default:
4007
+ description: >-
4008
+ Upstream OpenAI SDP exchange error. Pass-through: this endpoint forwards OpenAI's HTTP status verbatim
4009
+ (typically 4xx/5xx) and the body is OpenAI's raw error payload — NOT a JSON envelope generated by this
4010
+ server. Content-Type and exact shape are determined by OpenAI; clients MUST treat any non-200 response on
4011
+ this endpoint as opaque error text unless they explicitly detect the 502 JSON envelope above.
4012
+ content:
4013
+ application/json:
4014
+ schema: {}
4015
+ text/plain:
4016
+ schema:
4017
+ type: string
3246
4018
  webhooks: {}