@serviceai/api-spec 1.2.417 → 1.2.419

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 +791 -41
  2. package/openapi.yaml +720 -39
  3. package/package.json +1 -1
  4. package/types.d.ts +382 -33
package/openapi.json CHANGED
@@ -2,7 +2,7 @@
2
2
  "openapi": "3.1.0",
3
3
  "info": {
4
4
  "title": "ServiceAi API",
5
- "version": "1.2.417",
5
+ "version": "1.2.419",
6
6
  "description": "Source-of-truth API contract for ServiceAi. Generated from `shared/schema.ts` Zod schemas by `scripts/build-api-spec.ts`. Versioning policy: see `docs/api-versioning.md`. The hand-written docs/ios-contract.md and docs/lidar-mobile-upload-contract.md are human-readable design notes only — this spec is the canonical wire contract.",
7
7
  "license": {
8
8
  "name": "Proprietary",
@@ -59,6 +59,10 @@
59
59
  {
60
60
  "name": "schedule",
61
61
  "description": "When a person is expected to work (employee_shifts), and time-off requests against it. Separate from the job calendar by design."
62
+ },
63
+ {
64
+ "name": "account",
65
+ "description": "The signed-in person's own account: asking for it to be deleted."
62
66
  }
63
67
  ],
64
68
  "components": {
@@ -122,7 +126,7 @@
122
126
  "properties": {
123
127
  "apiVersion": {
124
128
  "type": "string",
125
- "example": "1.2.417"
129
+ "example": "1.2.419"
126
130
  },
127
131
  "minClientVersion": {
128
132
  "type": "string",
@@ -3182,6 +3186,80 @@
3182
3186
  "sdp"
3183
3187
  ]
3184
3188
  },
3189
+ "PunchTimeCheck": {
3190
+ "type": "object",
3191
+ "properties": {
3192
+ "reasons": {
3193
+ "type": "array",
3194
+ "items": {
3195
+ "type": "string",
3196
+ "enum": [
3197
+ "server_time",
3198
+ "unverified",
3199
+ "clock_changed",
3200
+ "too_old",
3201
+ "replaced_auto_close",
3202
+ "after_auto_close",
3203
+ "auto_close_kept"
3204
+ ]
3205
+ },
3206
+ "description": "server_time: the phone's time was more than 2 minutes from the server's, and the server's was recorded. unverified: the phone restarted before sending, so the time couldn't be checked; the phone's was kept. clock_changed: the phone's clock was changed by hand. Late stops (T6): too_old: a stop more than 12 hours old that couldn't be proven was recorded at the 12-hour limit. replaced_auto_close: a proven stop replaced the automatic close (autoAt). after_auto_close: a stop later than the automatic close; the automatic close was kept. auto_close_kept: a stop earlier than the automatic close that wasn't applied (keptBecause)."
3207
+ },
3208
+ "phoneAt": {
3209
+ "type": [
3210
+ "string",
3211
+ "null"
3212
+ ],
3213
+ "format": "date-time",
3214
+ "description": "The phone's own time for the tap."
3215
+ },
3216
+ "serverAt": {
3217
+ "type": [
3218
+ "string",
3219
+ "null"
3220
+ ],
3221
+ "format": "date-time",
3222
+ "description": "The tap by the server's clock (arrival minus tapElapsedMs); null when the phone couldn't say."
3223
+ },
3224
+ "receivedAt": {
3225
+ "type": "string",
3226
+ "format": "date-time",
3227
+ "description": "When the request reached the server."
3228
+ },
3229
+ "tapElapsedMs": {
3230
+ "type": [
3231
+ "integer",
3232
+ "null"
3233
+ ]
3234
+ },
3235
+ "verified": {
3236
+ "type": "boolean",
3237
+ "description": "True when the phone's stopwatch proved the tap's time."
3238
+ },
3239
+ "autoAt": {
3240
+ "type": "string",
3241
+ "format": "date-time",
3242
+ "description": "The automatic close a late stop replaced or was held against."
3243
+ },
3244
+ "keptBecause": {
3245
+ "type": "string",
3246
+ "enum": [
3247
+ "unverified",
3248
+ "office",
3249
+ "locked"
3250
+ ],
3251
+ "description": "Why a late stop wasn't applied: its time couldn't be proven, the office had already changed or approved the time, or the pay period is locked."
3252
+ }
3253
+ },
3254
+ "required": [
3255
+ "reasons",
3256
+ "phoneAt",
3257
+ "serverAt",
3258
+ "receivedAt",
3259
+ "tapElapsedMs"
3260
+ ],
3261
+ "description": "A tap whose time the office is asked to look at, with both times. It waits in Team › Needs attention."
3262
+ },
3185
3263
  "TimeEntry": {
3186
3264
  "type": "object",
3187
3265
  "properties": {
@@ -3317,6 +3395,64 @@
3317
3395
  "type": "integer",
3318
3396
  "description": "Unpaid break minutes recorded against this entry."
3319
3397
  },
3398
+ "startCheck": {
3399
+ "type": [
3400
+ "string",
3401
+ "null"
3402
+ ],
3403
+ "enum": [
3404
+ "away",
3405
+ "no_location"
3406
+ ],
3407
+ "description": "Job time started by hand away from the job ('away') or with no usable location ('no_location'); null for a normal start. The office decides it."
3408
+ },
3409
+ "startDistanceM": {
3410
+ "type": [
3411
+ "integer",
3412
+ "null"
3413
+ ],
3414
+ "description": "How far from the job's site center the start was, when startCheck is 'away'."
3415
+ },
3416
+ "startReview": {
3417
+ "type": [
3418
+ "string",
3419
+ "null"
3420
+ ],
3421
+ "enum": [
3422
+ "approved",
3423
+ "not_approved",
3424
+ "moved"
3425
+ ],
3426
+ "description": "The office's decision on a flagged start; null while it waits. 'not_approved' stays on the timesheet but off the job's costing."
3427
+ },
3428
+ "clockInCheck": {
3429
+ "allOf": [
3430
+ {
3431
+ "$ref": "#/components/schemas/PunchTimeCheck"
3432
+ },
3433
+ {
3434
+ "type": [
3435
+ "object",
3436
+ "null"
3437
+ ],
3438
+ "description": "Set when the start's time was checked and the office is asked to look; clockIn holds the time recorded."
3439
+ }
3440
+ ]
3441
+ },
3442
+ "clockOutCheck": {
3443
+ "allOf": [
3444
+ {
3445
+ "$ref": "#/components/schemas/PunchTimeCheck"
3446
+ },
3447
+ {
3448
+ "type": [
3449
+ "object",
3450
+ "null"
3451
+ ],
3452
+ "description": "The same for the stop; clockOut holds the time recorded."
3453
+ }
3454
+ ]
3455
+ },
3320
3456
  "createdAt": {
3321
3457
  "type": [
3322
3458
  "string",
@@ -3330,7 +3466,7 @@
3330
3466
  },
3331
3467
  "userName": {
3332
3468
  "type": "string",
3333
- "description": "Enrichment on /entries and /active?scope=team."
3469
+ "description": "Enrichment on /entries."
3334
3470
  }
3335
3471
  },
3336
3472
  "required": [
@@ -3536,6 +3672,34 @@
3536
3672
  "null"
3537
3673
  ]
3538
3674
  },
3675
+ "clockInCheck": {
3676
+ "allOf": [
3677
+ {
3678
+ "$ref": "#/components/schemas/PunchTimeCheck"
3679
+ },
3680
+ {
3681
+ "type": [
3682
+ "object",
3683
+ "null"
3684
+ ],
3685
+ "description": "Set when the Clock In tap's time was checked and the office is asked to look; clockIn holds the time recorded."
3686
+ }
3687
+ ]
3688
+ },
3689
+ "clockOutCheck": {
3690
+ "allOf": [
3691
+ {
3692
+ "$ref": "#/components/schemas/PunchTimeCheck"
3693
+ },
3694
+ {
3695
+ "type": [
3696
+ "object",
3697
+ "null"
3698
+ ],
3699
+ "description": "The same for the Clock Out tap; clockOut holds the time recorded."
3700
+ }
3701
+ ]
3702
+ },
3539
3703
  "editedBy": {
3540
3704
  "type": [
3541
3705
  "string",
@@ -3660,6 +3824,32 @@
3660
3824
  "MyDayResponse": {
3661
3825
  "type": "object",
3662
3826
  "properties": {
3827
+ "payrollAddon": {
3828
+ "type": "boolean",
3829
+ "description": "The company's time tracking (payroll) add-on. False: no Clock In/Out, no work days, schedules, time cards or pay; job time still runs. Then open is null, today is empty, shiftEnd is null, payroll.breakButton and locationTrail are false, and the payroll routes answer 403 PAYROLL_OFF. Not the same as payroll.enabled."
3830
+ },
3831
+ "payroll": {
3832
+ "type": "object",
3833
+ "properties": {
3834
+ "enabled": {
3835
+ "type": "boolean",
3836
+ "description": "A pay period is set (the company's pay rules are on)."
3837
+ },
3838
+ "breakButton": {
3839
+ "type": "boolean",
3840
+ "description": "Show the break button (false when this person's lunch comes off automatically)."
3841
+ },
3842
+ "breaksPaid": {
3843
+ "type": "boolean"
3844
+ }
3845
+ },
3846
+ "required": [
3847
+ "enabled",
3848
+ "breakButton",
3849
+ "breaksPaid"
3850
+ ],
3851
+ "description": "The company's pay rules as they touch this person."
3852
+ },
3663
3853
  "dayModel": {
3664
3854
  "type": "boolean",
3665
3855
  "description": "True once this person is on the day model."
@@ -3745,6 +3935,8 @@
3745
3935
  }
3746
3936
  },
3747
3937
  "required": [
3938
+ "payrollAddon",
3939
+ "payroll",
3748
3940
  "dayModel",
3749
3941
  "open",
3750
3942
  "today",
@@ -3935,6 +4127,28 @@
3935
4127
  "autoClockEnabled": {
3936
4128
  "type": "boolean",
3937
4129
  "description": "The user's auto-clock toggle at crossing time. False → the crossing is logged but never acted on."
4130
+ },
4131
+ "tapElapsedMs": {
4132
+ "type": [
4133
+ "integer",
4134
+ "null"
4135
+ ],
4136
+ "minimum": 0,
4137
+ "description": "Milliseconds from the tap to this send, from the phone's monotonic clock (a stopwatch the person can't change). An offline replay sends the time since the tap. The server's tap time is its own clock when the request arrives minus this. Within 2 minutes of the phone's time, the phone's time stands (never later than the tap); further apart, the server's tap time is recorded and the office is asked to look. A Clock Out or job time stop it proves this way keeps its real time however old; without it, a stop older than 12 hours is recorded at the 12-hour limit and flagged (T6). Send it with every queued tap, clock-outs included."
4138
+ },
4139
+ "tapTimeUnverified": {
4140
+ "type": [
4141
+ "boolean",
4142
+ "null"
4143
+ ],
4144
+ "description": "True when the phone restarted between the tap and this send, so there is no stopwatch reading (send no tapElapsedMs). A tap more than 2 minutes old keeps the phone's time and is flagged for the office."
4145
+ },
4146
+ "deviceClockChanged": {
4147
+ "type": [
4148
+ "boolean",
4149
+ "null"
4150
+ ],
4151
+ "description": "True when the phone's clock was changed by hand since the phone last synced. The tap is flagged for the office."
3938
4152
  }
3939
4153
  },
3940
4154
  "required": [
@@ -3942,7 +4156,7 @@
3942
4156
  "kind",
3943
4157
  "occurredAt"
3944
4158
  ],
3945
- "description": "One fence crossing as the device saw it."
4159
+ "description": "One fence crossing as the device saw it. With tapElapsedMs (the time since the crossing), the server checks occurredAt against its own clock before the resolver uses it, and the entry the crossing opens or closes carries the check. A departure it proves isn't held to the 12-hour limit (T6)."
3946
4160
  },
3947
4161
  "CrossingsAck": {
3948
4162
  "type": "object",
@@ -4140,6 +4354,10 @@
4140
4354
  "canEdit": {
4141
4355
  "type": "boolean",
4142
4356
  "description": "True only for the primary account."
4357
+ },
4358
+ "jobSiteTracking": {
4359
+ "type": "boolean",
4360
+ "description": "The caller's own switch, set by the office (owner's O9). False: watch no job-site fences and use no location for job time; job time comes only from Start/Stop job time. The office fence, Clock In/Out and the company switches are unaffected. Absent (an older server): treat as true."
4143
4361
  }
4144
4362
  },
4145
4363
  "required": [
@@ -4377,6 +4595,10 @@
4377
4595
  "format": "date",
4378
4596
  "description": "The org-local date, which is not necessarily the device's."
4379
4597
  },
4598
+ "payrollAddon": {
4599
+ "type": "boolean",
4600
+ "description": "False when the company has the time tracking add-on off: no shifts, hours or time off come back, and nothing covers now."
4601
+ },
4380
4602
  "hours": {
4381
4603
  "type": "object",
4382
4604
  "additionalProperties": {
@@ -4488,6 +4710,7 @@
4488
4710
  "from",
4489
4711
  "to",
4490
4712
  "today",
4713
+ "payrollAddon",
4491
4714
  "hours",
4492
4715
  "attendance",
4493
4716
  "shifts",
@@ -4495,6 +4718,82 @@
4495
4718
  "recentTimeOff"
4496
4719
  ],
4497
4720
  "description": "When I should be in, plus what I actually worked. The clock-in expectation lives here."
4721
+ },
4722
+ "OnTheClockRow": {
4723
+ "type": "object",
4724
+ "properties": {
4725
+ "userId": {
4726
+ "type": "string"
4727
+ },
4728
+ "userName": {
4729
+ "type": "string"
4730
+ },
4731
+ "entryId": {
4732
+ "type": [
4733
+ "integer",
4734
+ "null"
4735
+ ],
4736
+ "description": "The open site entry; null between sites (a day with nothing open)."
4737
+ },
4738
+ "jobId": {
4739
+ "type": [
4740
+ "integer",
4741
+ "null"
4742
+ ]
4743
+ },
4744
+ "siteType": {
4745
+ "type": "string",
4746
+ "enum": [
4747
+ "job",
4748
+ "office",
4749
+ "none"
4750
+ ],
4751
+ "description": "'none' between sites."
4752
+ },
4753
+ "jobName": {
4754
+ "type": [
4755
+ "string",
4756
+ "null"
4757
+ ]
4758
+ },
4759
+ "since": {
4760
+ "type": "string",
4761
+ "format": "date-time",
4762
+ "description": "When the work day began, else when the entry did."
4763
+ }
4764
+ },
4765
+ "required": [
4766
+ "userId",
4767
+ "userName",
4768
+ "entryId",
4769
+ "jobId",
4770
+ "siteType",
4771
+ "jobName",
4772
+ "since"
4773
+ ],
4774
+ "description": "One person on the clock (server/onTheClock.ts). Job time with no paid day behind it is left out: that person is at a job, not on the clock."
4775
+ },
4776
+ "AccountDeletionRequest": {
4777
+ "type": "object",
4778
+ "properties": {
4779
+ "status": {
4780
+ "type": "string",
4781
+ "enum": [
4782
+ "pending",
4783
+ "completed",
4784
+ "cancelled"
4785
+ ]
4786
+ },
4787
+ "requestedAt": {
4788
+ "type": "string",
4789
+ "format": "date-time"
4790
+ }
4791
+ },
4792
+ "required": [
4793
+ "status",
4794
+ "requestedAt"
4795
+ ],
4796
+ "description": "The signed-in person's request to have their account deleted. pending: waiting for the company; completed: the company marked it done; cancelled: the company cancelled it (asking again starts a new one)."
4498
4797
  }
4499
4798
  },
4500
4799
  "parameters": {}
@@ -6490,6 +6789,25 @@
6490
6789
  "auto",
6491
6790
  "manual"
6492
6791
  ]
6792
+ },
6793
+ "duplicateOf": {
6794
+ "type": "object",
6795
+ "properties": {
6796
+ "roomId": {
6797
+ "type": "integer",
6798
+ "description": "Id of the room whose copy of this doorway is drawn."
6799
+ },
6800
+ "doorIndex": {
6801
+ "type": "integer",
6802
+ "minimum": 0,
6803
+ "description": "Index into that room's `doors[]`."
6804
+ }
6805
+ },
6806
+ "required": [
6807
+ "roomId",
6808
+ "doorIndex"
6809
+ ],
6810
+ "description": "Present only on the later-scanned room's copy of a doorway two rooms in this response share (the rooms are linked by a door link, or their doors coincide on one wall). Skip this door's glyph and keep its wall gap; the door it names is the one to draw. Absent on every other door. Read-time only: never send it back, it is never stored."
6493
6811
  }
6494
6812
  },
6495
6813
  "required": [
@@ -6498,7 +6816,7 @@
6498
6816
  "height"
6499
6817
  ]
6500
6818
  },
6501
- "description": "RoomPlan-classified doors. Distinct from `windows[]` and `openings[]`; append-room `entryDoorwayId` tokens of the form `…-door-<idx>` resolve into THIS array only."
6819
+ "description": "RoomPlan-classified doors. Distinct from `windows[]` and `openings[]`; append-room `entryDoorwayId` tokens of the form `…-door-<idx>` resolve into THIS array only. A door carrying `duplicateOf` is the hidden copy of a shared doorway (one door per doorway, from the room scanned first)."
6502
6820
  },
6503
6821
  "windows": {
6504
6822
  "type": "array",
@@ -6860,6 +7178,25 @@
6860
7178
  "auto",
6861
7179
  "manual"
6862
7180
  ]
7181
+ },
7182
+ "duplicateOf": {
7183
+ "type": "object",
7184
+ "properties": {
7185
+ "roomId": {
7186
+ "type": "integer",
7187
+ "description": "Id of the room whose copy of this doorway is drawn."
7188
+ },
7189
+ "doorIndex": {
7190
+ "type": "integer",
7191
+ "minimum": 0,
7192
+ "description": "Index into that room's `doors[]`."
7193
+ }
7194
+ },
7195
+ "required": [
7196
+ "roomId",
7197
+ "doorIndex"
7198
+ ],
7199
+ "description": "Present only on the later-scanned room's copy of a doorway two rooms in this response share (the rooms are linked by a door link, or their doors coincide on one wall). Skip this door's glyph and keep its wall gap; the door it names is the one to draw. Absent on every other door. Read-time only: never send it back, it is never stored."
6863
7200
  }
6864
7201
  },
6865
7202
  "required": [
@@ -6868,7 +7205,7 @@
6868
7205
  "height"
6869
7206
  ]
6870
7207
  },
6871
- "description": "RoomPlan-classified doors. Distinct from `windows[]` and `openings[]`; append-room `entryDoorwayId` tokens of the form `…-door-<idx>` resolve into THIS array only."
7208
+ "description": "RoomPlan-classified doors. Distinct from `windows[]` and `openings[]`; append-room `entryDoorwayId` tokens of the form `…-door-<idx>` resolve into THIS array only. A door carrying `duplicateOf` is the hidden copy of a shared doorway (one door per doorway, from the room scanned first)."
6872
7209
  },
6873
7210
  "windows": {
6874
7211
  "type": "array",
@@ -7985,7 +8322,29 @@
7985
8322
  "null"
7986
8323
  ],
7987
8324
  "format": "date-time",
7988
- "description": "When the tap happened. The server clamps it."
8325
+ "description": "When the tap happened, by the phone's clock. The server checks it against its own (tapElapsedMs) and clamps it. From the web (device 'web') it is ignored: the server's receipt time is used."
8326
+ },
8327
+ "tapElapsedMs": {
8328
+ "type": [
8329
+ "integer",
8330
+ "null"
8331
+ ],
8332
+ "minimum": 0,
8333
+ "description": "Milliseconds from the tap to this send, from the phone's monotonic clock (a stopwatch the person can't change). An offline replay sends the time since the tap. The server's tap time is its own clock when the request arrives minus this. Within 2 minutes of the phone's time, the phone's time stands (never later than the tap); further apart, the server's tap time is recorded and the office is asked to look. A Clock Out or job time stop it proves this way keeps its real time however old; without it, a stop older than 12 hours is recorded at the 12-hour limit and flagged (T6). Send it with every queued tap, clock-outs included."
8334
+ },
8335
+ "tapTimeUnverified": {
8336
+ "type": [
8337
+ "boolean",
8338
+ "null"
8339
+ ],
8340
+ "description": "True when the phone restarted between the tap and this send, so there is no stopwatch reading (send no tapElapsedMs). A tap more than 2 minutes old keeps the phone's time and is flagged for the office."
8341
+ },
8342
+ "deviceClockChanged": {
8343
+ "type": [
8344
+ "boolean",
8345
+ "null"
8346
+ ],
8347
+ "description": "True when the phone's clock was changed by hand since the phone last synced. The tap is flagged for the office."
7989
8348
  },
7990
8349
  "notes": {
7991
8350
  "type": [
@@ -8031,7 +8390,7 @@
8031
8390
  }
8032
8391
  },
8033
8392
  "403": {
8034
- "description": "Time tracking access denied, or no organization on the session.",
8393
+ "description": "Time tracking access denied, no organization on the session, or code PAYROLL_OFF: the company has the time tracking add-on off (GET /api/time-tracking/day says payrollAddon: false). Don't retry; hide Clock In/Out and the other payroll controls.",
8035
8394
  "content": {
8036
8395
  "application/json": {
8037
8396
  "schema": {
@@ -8094,7 +8453,7 @@
8094
8453
  },
8095
8454
  "/api/time-tracking/day/clock-out": {
8096
8455
  "post": {
8097
- "summary": "End my day. Closes every open entry with it.",
8456
+ "summary": "End my day: the paid day only. Job time (fence or job timer) runs until it is stopped or the phone reports leaving, and is never paid past Clock Out.",
8098
8457
  "tags": [
8099
8458
  "time-tracking"
8100
8459
  ],
@@ -8120,6 +8479,7 @@
8120
8479
  "description": "Send `1` to declare this client understands work days. The first request carrying it moves the caller onto the day model permanently (users.time_clock_day_model_since); from then on their fence arrivals only open entries inside an open day."
8121
8480
  }
8122
8481
  ],
8482
+ "description": "A late Clock Out (T6): with tapElapsedMs proving its time, it keeps its real time however old. When the safety net already closed the day (the automatic close), a proven Clock Out earlier than that close replaces it (replacedAutoClose, flagged for the office); anything else about that day is flagged for the office and changes nothing. A proven Clock Out from before the running day started never ends the running day.",
8123
8483
  "requestBody": {
8124
8484
  "content": {
8125
8485
  "application/json": {
@@ -8166,7 +8526,29 @@
8166
8526
  "null"
8167
8527
  ],
8168
8528
  "format": "date-time",
8169
- "description": "When the tap happened. The server clamps it."
8529
+ "description": "When the tap happened, by the phone's clock. The server checks it against its own (tapElapsedMs) and clamps it. From the web (device 'web') it is ignored: the server's receipt time is used."
8530
+ },
8531
+ "tapElapsedMs": {
8532
+ "type": [
8533
+ "integer",
8534
+ "null"
8535
+ ],
8536
+ "minimum": 0,
8537
+ "description": "Milliseconds from the tap to this send, from the phone's monotonic clock (a stopwatch the person can't change). An offline replay sends the time since the tap. The server's tap time is its own clock when the request arrives minus this. Within 2 minutes of the phone's time, the phone's time stands (never later than the tap); further apart, the server's tap time is recorded and the office is asked to look. A Clock Out or job time stop it proves this way keeps its real time however old; without it, a stop older than 12 hours is recorded at the 12-hour limit and flagged (T6). Send it with every queued tap, clock-outs included."
8538
+ },
8539
+ "tapTimeUnverified": {
8540
+ "type": [
8541
+ "boolean",
8542
+ "null"
8543
+ ],
8544
+ "description": "True when the phone restarted between the tap and this send, so there is no stopwatch reading (send no tapElapsedMs). A tap more than 2 minutes old keeps the phone's time and is flagged for the office."
8545
+ },
8546
+ "deviceClockChanged": {
8547
+ "type": [
8548
+ "boolean",
8549
+ "null"
8550
+ ],
8551
+ "description": "True when the phone's clock was changed by hand since the phone last synced. The tap is flagged for the office."
8170
8552
  },
8171
8553
  "note": {
8172
8554
  "type": [
@@ -8182,7 +8564,7 @@
8182
8564
  },
8183
8565
  "responses": {
8184
8566
  "200": {
8185
- "description": "Day ended.",
8567
+ "description": "Day ended, or the automatic close of the day this tap belongs to replaced (replacedAutoClose).",
8186
8568
  "content": {
8187
8569
  "application/json": {
8188
8570
  "schema": {
@@ -8201,13 +8583,12 @@
8201
8583
  }
8202
8584
  ]
8203
8585
  },
8204
- "closedEntries": {
8205
- "type": "integer"
8586
+ "replacedAutoClose": {
8587
+ "type": "boolean"
8206
8588
  }
8207
8589
  },
8208
8590
  "required": [
8209
- "day",
8210
- "closedEntries"
8591
+ "day"
8211
8592
  ]
8212
8593
  }
8213
8594
  }
@@ -8224,7 +8605,7 @@
8224
8605
  }
8225
8606
  },
8226
8607
  "403": {
8227
- "description": "Time tracking access denied, or no organization on the session.",
8608
+ "description": "Time tracking access denied, no organization on the session, or code PAYROLL_OFF: the company has the time tracking add-on off (GET /api/time-tracking/day says payrollAddon: false). Don't retry; hide Clock In/Out and the other payroll controls.",
8228
8609
  "content": {
8229
8610
  "application/json": {
8230
8611
  "schema": {
@@ -8234,7 +8615,7 @@
8234
8615
  }
8235
8616
  },
8236
8617
  "404": {
8237
- "description": "No day is running (code NO_OPEN_DAY).",
8618
+ "description": "No day is running (code NO_OPEN_DAY). Also when the tap was only flagged for the office.",
8238
8619
  "content": {
8239
8620
  "application/json": {
8240
8621
  "schema": {
@@ -8244,7 +8625,7 @@
8244
8625
  }
8245
8626
  },
8246
8627
  "409": {
8247
- "description": "The day was already closed.",
8628
+ "description": "The day was already closed; or code DAY_CLOSED_AUTOMATICALLY: the tap belongs to an earlier day the safety net closed, and was flagged for the office.",
8248
8629
  "content": {
8249
8630
  "application/json": {
8250
8631
  "schema": {
@@ -8512,7 +8893,7 @@
8512
8893
  ],
8513
8894
  "responses": {
8514
8895
  "200": {
8515
- "description": "Fences, office first when requested.",
8896
+ "description": "Fences, office first when requested. No job fences for someone the office turned job-site tracking off for (clock-config jobSiteTracking false), except with includeClosed for the manual picker.",
8516
8897
  "content": {
8517
8898
  "application/json": {
8518
8899
  "schema": {
@@ -8559,7 +8940,7 @@
8559
8940
  },
8560
8941
  "/api/time-tracking/active": {
8561
8942
  "get": {
8562
- "summary": "My open entries right now (or the team's).",
8943
+ "summary": "My open entries right now, or who is on the clock.",
8563
8944
  "tags": [
8564
8945
  "time-tracking"
8565
8946
  ],
@@ -8591,22 +8972,33 @@
8591
8972
  "schema": {
8592
8973
  "type": "string",
8593
8974
  "enum": [
8594
- "team"
8975
+ "team",
8976
+ "me"
8595
8977
  ]
8596
8978
  },
8597
- "description": "Everyone on the clock. Requires time_tracking.view_all."
8979
+ "description": "`team`: everyone on the clock, one OnTheClockRow per person; requires time_tracking.view_all. `me`: the caller's own row while on the clock. Without scope: the caller's open entries."
8598
8980
  }
8599
8981
  ],
8600
8982
  "responses": {
8601
8983
  "200": {
8602
- "description": "Open entries. Normally zero or one for a single user.",
8984
+ "description": "Without scope: open entries, normally zero or one. With scope: one OnTheClockRow per person on the clock.",
8603
8985
  "content": {
8604
8986
  "application/json": {
8605
8987
  "schema": {
8606
- "type": "array",
8607
- "items": {
8608
- "$ref": "#/components/schemas/TimeEntry"
8609
- }
8988
+ "anyOf": [
8989
+ {
8990
+ "type": "array",
8991
+ "items": {
8992
+ "$ref": "#/components/schemas/TimeEntry"
8993
+ }
8994
+ },
8995
+ {
8996
+ "type": "array",
8997
+ "items": {
8998
+ "$ref": "#/components/schemas/OnTheClockRow"
8999
+ }
9000
+ }
9001
+ ]
8610
9002
  }
8611
9003
  }
8612
9004
  }
@@ -8772,8 +9164,8 @@
8772
9164
  },
8773
9165
  "/api/time-tracking/clock-in": {
8774
9166
  "post": {
8775
- "summary": "Clock in to a job or the office by hand.",
8776
- "description": "A deliberate button press. On the day model a press with no day running starts the day at that moment; a fence-driven clock-in from an old-model client cannot, and is refused.",
9167
+ "summary": "Start time on a job or at the office by hand.",
9168
+ "description": "A deliberate button press. The entry joins the person's work day when one is running. With none running: a job timer press (startDay: false) records job time only and never starts the paid day; a Clock In press (startDay omitted: older iPhones' job picker, the office's \"Clock X in\") starts the day at that moment; an automatic clock-in is refused with NO_OPEN_DAY, because the fence crossing already records the arrival. A person starting their own job time away from the job's site, or with no usable location, is asked first (send canConfirmAway: 409 JOB_TIME_AWAY carries the question; resend with confirmAway once they say yes) and the entry is flagged (startCheck) for the office to approve, move or not count. Older apps start straight away, flagged the same.",
8777
9169
  "tags": [
8778
9170
  "time-tracking"
8779
9171
  ],
@@ -8833,6 +9225,11 @@
8833
9225
  "type": "string",
8834
9226
  "description": "Clock someone else in. Requires time_tracking.edit_all."
8835
9227
  },
9228
+ "note": {
9229
+ "type": "string",
9230
+ "maxLength": 2000,
9231
+ "description": "Why, when clocking someone else in. Required with userId."
9232
+ },
8836
9233
  "lat": {
8837
9234
  "type": "number"
8838
9235
  },
@@ -8846,6 +9243,10 @@
8846
9243
  "auto"
8847
9244
  ]
8848
9245
  },
9246
+ "startDay": {
9247
+ "type": "boolean",
9248
+ "description": "false: a job timer press, job time only; it never starts the paid day. Omitted: a Clock In press starts the day when none is running."
9249
+ },
8849
9250
  "notes": {
8850
9251
  "type": "string"
8851
9252
  },
@@ -8858,7 +9259,7 @@
8858
9259
  "occurredAt": {
8859
9260
  "type": "string",
8860
9261
  "format": "date-time",
8861
- "description": "When the tech actually arrived. The server clamps it."
9262
+ "description": "When the tech actually arrived, by the phone's clock. The server checks it against its own (tapElapsedMs) and clamps it. From the web (device 'web') it is ignored: the server's receipt time is used."
8862
9263
  },
8863
9264
  "accuracyM": {
8864
9265
  "type": [
@@ -8877,6 +9278,36 @@
8877
9278
  "web",
8878
9279
  "android"
8879
9280
  ]
9281
+ },
9282
+ "canConfirmAway": {
9283
+ "type": "boolean",
9284
+ "description": "This app can ask the person before starting job time away from the job (it handles 409 JOB_TIME_AWAY)."
9285
+ },
9286
+ "confirmAway": {
9287
+ "type": "boolean",
9288
+ "description": "The person said yes to starting away from the job."
9289
+ },
9290
+ "tapElapsedMs": {
9291
+ "type": [
9292
+ "integer",
9293
+ "null"
9294
+ ],
9295
+ "minimum": 0,
9296
+ "description": "Milliseconds from the tap to this send, from the phone's monotonic clock (a stopwatch the person can't change). An offline replay sends the time since the tap. The server's tap time is its own clock when the request arrives minus this. Within 2 minutes of the phone's time, the phone's time stands (never later than the tap); further apart, the server's tap time is recorded and the office is asked to look. A Clock Out or job time stop it proves this way keeps its real time however old; without it, a stop older than 12 hours is recorded at the 12-hour limit and flagged (T6). Send it with every queued tap, clock-outs included."
9297
+ },
9298
+ "tapTimeUnverified": {
9299
+ "type": [
9300
+ "boolean",
9301
+ "null"
9302
+ ],
9303
+ "description": "True when the phone restarted between the tap and this send, so there is no stopwatch reading (send no tapElapsedMs). A tap more than 2 minutes old keeps the phone's time and is flagged for the office."
9304
+ },
9305
+ "deviceClockChanged": {
9306
+ "type": [
9307
+ "boolean",
9308
+ "null"
9309
+ ],
9310
+ "description": "True when the phone's clock was changed by hand since the phone last synced. The tap is flagged for the office."
8880
9311
  }
8881
9312
  }
8882
9313
  }
@@ -8895,7 +9326,7 @@
8895
9326
  }
8896
9327
  },
8897
9328
  "400": {
8898
- "description": "Validation failure, or jobId missing on a job clock-in.",
9329
+ "description": "Validation failure, jobId missing on a job clock-in, or no note when clocking someone else in.",
8899
9330
  "content": {
8900
9331
  "application/json": {
8901
9332
  "schema": {
@@ -8915,7 +9346,7 @@
8915
9346
  }
8916
9347
  },
8917
9348
  "403": {
8918
- "description": "Time tracking access denied, or no organization on the session.",
9349
+ "description": "Time tracking access denied, or no organization on the session. PAYROLL_OFF: a Clock In press that would start the day at a company with the time tracking add-on off. JOB_TIME_OFF: the company has both the time tracking add-on and location tracking off, so job time isn't used; stopping running job time still works.",
8919
9350
  "content": {
8920
9351
  "application/json": {
8921
9352
  "schema": {
@@ -8935,7 +9366,7 @@
8935
9366
  }
8936
9367
  },
8937
9368
  "409": {
8938
- "description": "Already clocked in at this site (code ALREADY_CLOCKED_IN_SAME_JOB). The body carries the existing entry — adopt it.",
9369
+ "description": "ALREADY_CLOCKED_IN_SAME_JOB: already at this site; the body carries the existing entry — adopt it. ALREADY_CLOCKED_IN_OTHER_JOB: an automatic clock-in while another entry is open; the body carries it. NO_OPEN_DAY: an automatic clock-in with no work day running; the fence crossing records the arrival. JOB_SITE_TRACKING_OFF: an automatic clock-in for someone the office turned job-site tracking off for; start job time by hand. PERIOD_LOCKED: an admin clock-in inside a locked pay period. JOB_TIME_AWAY: starting job time away from the job (reason 'away', with distanceM) or with no usable location (reason 'no_location'); show message as the question and resend with confirmAway: true.",
8939
9370
  "content": {
8940
9371
  "application/json": {
8941
9372
  "schema": {
@@ -8949,12 +9380,30 @@
8949
9380
  },
8950
9381
  "entry": {
8951
9382
  "$ref": "#/components/schemas/TimeEntry"
9383
+ },
9384
+ "reason": {
9385
+ "type": "string",
9386
+ "enum": [
9387
+ "away",
9388
+ "no_location"
9389
+ ]
9390
+ },
9391
+ "distanceM": {
9392
+ "type": [
9393
+ "integer",
9394
+ "null"
9395
+ ]
9396
+ },
9397
+ "jobName": {
9398
+ "type": [
9399
+ "string",
9400
+ "null"
9401
+ ]
8952
9402
  }
8953
9403
  },
8954
9404
  "required": [
8955
9405
  "message",
8956
- "code",
8957
- "entry"
9406
+ "code"
8958
9407
  ]
8959
9408
  }
8960
9409
  }
@@ -9053,7 +9502,7 @@
9053
9502
  "occurredAt": {
9054
9503
  "type": "string",
9055
9504
  "format": "date-time",
9056
- "description": "When the tech actually left. Clamped to [entry.clockIn, now]."
9505
+ "description": "When the tech actually left, by the phone's clock. Checked against the server's (tapElapsedMs) and clamped to [entry.clockIn, now], and to the last 12 hours unless tapElapsedMs proves it (T6). From the web (device 'web') it is ignored: the server's receipt time is used."
9057
9506
  },
9058
9507
  "accuracyM": {
9059
9508
  "type": [
@@ -9072,6 +9521,28 @@
9072
9521
  "web",
9073
9522
  "android"
9074
9523
  ]
9524
+ },
9525
+ "tapElapsedMs": {
9526
+ "type": [
9527
+ "integer",
9528
+ "null"
9529
+ ],
9530
+ "minimum": 0,
9531
+ "description": "Milliseconds from the tap to this send, from the phone's monotonic clock (a stopwatch the person can't change). An offline replay sends the time since the tap. The server's tap time is its own clock when the request arrives minus this. Within 2 minutes of the phone's time, the phone's time stands (never later than the tap); further apart, the server's tap time is recorded and the office is asked to look. A Clock Out or job time stop it proves this way keeps its real time however old; without it, a stop older than 12 hours is recorded at the 12-hour limit and flagged (T6). Send it with every queued tap, clock-outs included."
9532
+ },
9533
+ "tapTimeUnverified": {
9534
+ "type": [
9535
+ "boolean",
9536
+ "null"
9537
+ ],
9538
+ "description": "True when the phone restarted between the tap and this send, so there is no stopwatch reading (send no tapElapsedMs). A tap more than 2 minutes old keeps the phone's time and is flagged for the office."
9539
+ },
9540
+ "deviceClockChanged": {
9541
+ "type": [
9542
+ "boolean",
9543
+ "null"
9544
+ ],
9545
+ "description": "True when the phone's clock was changed by hand since the phone last synced. The tap is flagged for the office."
9075
9546
  }
9076
9547
  }
9077
9548
  }
@@ -9080,7 +9551,7 @@
9080
9551
  },
9081
9552
  "responses": {
9082
9553
  "200": {
9083
- "description": "Entry closed.",
9554
+ "description": "Entry closed; or, for a stop tapElapsedMs proves that is earlier than the automatic close of the job time there, that close replaced (T6).",
9084
9555
  "content": {
9085
9556
  "application/json": {
9086
9557
  "schema": {
@@ -9120,7 +9591,7 @@
9120
9591
  }
9121
9592
  },
9122
9593
  "404": {
9123
- "description": "No open entry at that site.",
9594
+ "description": "No open entry at that site. Also when the stop belongs to job time the safety net closed and was only flagged for the office.",
9124
9595
  "content": {
9125
9596
  "application/json": {
9126
9597
  "schema": {
@@ -9145,7 +9616,7 @@
9145
9616
  "/api/time-tracking/switch": {
9146
9617
  "post": {
9147
9618
  "summary": "Move my clock to another job in one atomic call.",
9148
- "description": "Closes the caller's open entry and opens one on the target. One request instead of clock-out + clock-in, so an offline drop between the two cannot strand the tech clocked out. Own entry only.",
9619
+ "description": "Closes the caller's open entry and opens one on the target. One request instead of clock-out + clock-in, so an offline drop between the two cannot strand the tech clocked out. Own entry only. Moving job time to a job the person isn't at, or with no usable location, is asked first (send canConfirmAway: 409 JOB_TIME_AWAY carries the question; resend with confirmAway once they say yes) and the new entry is flagged (startCheck) for the office. Older apps move straight away, flagged the same. switchAt is a time the person picked, so earlier than the tap is normal. With tapElapsedMs it is never later than the tap by the server's clock, and more than 2 minutes later is replaced by the tap time and flagged (opened.clockInCheck). tapTimeUnverified doesn't apply; deviceClockChanged flags it.",
9149
9620
  "tags": [
9150
9621
  "time-tracking"
9151
9622
  ],
@@ -9180,6 +9651,43 @@
9180
9651
  },
9181
9652
  "lng": {
9182
9653
  "type": "number"
9654
+ },
9655
+ "accuracyM": {
9656
+ "type": [
9657
+ "number",
9658
+ "null"
9659
+ ],
9660
+ "minimum": 0
9661
+ },
9662
+ "canConfirmAway": {
9663
+ "type": "boolean",
9664
+ "description": "This app can ask the person before moving job time to a job they aren't at (it handles 409 JOB_TIME_AWAY)."
9665
+ },
9666
+ "confirmAway": {
9667
+ "type": "boolean",
9668
+ "description": "The person said yes to moving it anyway."
9669
+ },
9670
+ "tapElapsedMs": {
9671
+ "type": [
9672
+ "integer",
9673
+ "null"
9674
+ ],
9675
+ "minimum": 0,
9676
+ "description": "Milliseconds from the tap to this send, from the phone's monotonic clock (a stopwatch the person can't change). An offline replay sends the time since the tap. The server's tap time is its own clock when the request arrives minus this. Within 2 minutes of the phone's time, the phone's time stands (never later than the tap); further apart, the server's tap time is recorded and the office is asked to look. A Clock Out or job time stop it proves this way keeps its real time however old; without it, a stop older than 12 hours is recorded at the 12-hour limit and flagged (T6). Send it with every queued tap, clock-outs included."
9677
+ },
9678
+ "tapTimeUnverified": {
9679
+ "type": [
9680
+ "boolean",
9681
+ "null"
9682
+ ],
9683
+ "description": "True when the phone restarted between the tap and this send, so there is no stopwatch reading (send no tapElapsedMs). A tap more than 2 minutes old keeps the phone's time and is flagged for the office."
9684
+ },
9685
+ "deviceClockChanged": {
9686
+ "type": [
9687
+ "boolean",
9688
+ "null"
9689
+ ],
9690
+ "description": "True when the phone's clock was changed by hand since the phone last synced. The tap is flagged for the office."
9183
9691
  }
9184
9692
  },
9185
9693
  "required": [
@@ -9237,7 +9745,7 @@
9237
9745
  }
9238
9746
  },
9239
9747
  "403": {
9240
- "description": "Time tracking access denied, or no organization on the session.",
9748
+ "description": "Time tracking access denied, or no organization on the session. JOB_TIME_OFF: the company has both the time tracking add-on and location tracking off, so job time isn't used; stopping running job time still works.",
9241
9749
  "content": {
9242
9750
  "application/json": {
9243
9751
  "schema": {
@@ -9256,6 +9764,47 @@
9256
9764
  }
9257
9765
  }
9258
9766
  },
9767
+ "409": {
9768
+ "description": "PERIOD_LOCKED: the switch time is inside a locked pay period. JOB_TIME_AWAY: the person isn't at the target job (reason 'away', with distanceM) or has no usable location (reason 'no_location'); nothing moved. Show message as the question and resend with confirmAway: true.",
9769
+ "content": {
9770
+ "application/json": {
9771
+ "schema": {
9772
+ "type": "object",
9773
+ "properties": {
9774
+ "message": {
9775
+ "type": "string"
9776
+ },
9777
+ "code": {
9778
+ "type": "string"
9779
+ },
9780
+ "reason": {
9781
+ "type": "string",
9782
+ "enum": [
9783
+ "away",
9784
+ "no_location"
9785
+ ]
9786
+ },
9787
+ "distanceM": {
9788
+ "type": [
9789
+ "integer",
9790
+ "null"
9791
+ ]
9792
+ },
9793
+ "jobName": {
9794
+ "type": [
9795
+ "string",
9796
+ "null"
9797
+ ]
9798
+ }
9799
+ },
9800
+ "required": [
9801
+ "message",
9802
+ "code"
9803
+ ]
9804
+ }
9805
+ }
9806
+ }
9807
+ },
9259
9808
  "426": {
9260
9809
  "description": "Client major below minClientVersion.",
9261
9810
  "content": {
@@ -9683,7 +10232,7 @@
9683
10232
  }
9684
10233
  },
9685
10234
  "403": {
9686
- "description": "Time tracking access denied, or no organization on the session.",
10235
+ "description": "Time tracking access denied, no organization on the session, or code PAYROLL_OFF: the company has the time tracking add-on off (GET /api/time-tracking/day says payrollAddon: false). Don't retry; hide Clock In/Out and the other payroll controls.",
9687
10236
  "content": {
9688
10237
  "application/json": {
9689
10238
  "schema": {
@@ -9968,6 +10517,16 @@
9968
10517
  }
9969
10518
  }
9970
10519
  },
10520
+ "403": {
10521
+ "description": "Time tracking access denied, no organization on the session, or code PAYROLL_OFF: the company has the time tracking add-on off (GET /api/time-tracking/day says payrollAddon: false). Don't retry; hide Clock In/Out and the other payroll controls.",
10522
+ "content": {
10523
+ "application/json": {
10524
+ "schema": {
10525
+ "$ref": "#/components/schemas/ErrorEnvelope"
10526
+ }
10527
+ }
10528
+ }
10529
+ },
9971
10530
  "409": {
9972
10531
  "description": "A request for those dates is already waiting.",
9973
10532
  "content": {
@@ -9990,6 +10549,197 @@
9990
10549
  }
9991
10550
  }
9992
10551
  }
10552
+ },
10553
+ "/api/account/deletion-request": {
10554
+ "post": {
10555
+ "summary": "Ask for my account to be deleted.",
10556
+ "description": "Nothing is deleted by this call. The request goes to the company's owner and Team admins (bell, push and email) and to ServiceAi support (email); the company removes the account. Asking again while a request is pending answers that request with 200 and creates nothing; everyone is reminded at most once a day. The reason is never put in a push.",
10557
+ "tags": [
10558
+ "account"
10559
+ ],
10560
+ "parameters": [
10561
+ {
10562
+ "name": "X-Client-Version",
10563
+ "in": "header",
10564
+ "required": false,
10565
+ "schema": {
10566
+ "type": "string",
10567
+ "example": "1.0.0"
10568
+ },
10569
+ "description": "Semver of the calling app build. Drives the 426 / soft-upgrade handshake."
10570
+ }
10571
+ ],
10572
+ "requestBody": {
10573
+ "content": {
10574
+ "application/json": {
10575
+ "schema": {
10576
+ "type": "object",
10577
+ "properties": {
10578
+ "reason": {
10579
+ "type": [
10580
+ "string",
10581
+ "null"
10582
+ ],
10583
+ "maxLength": 1000,
10584
+ "description": "Optional. Trimmed; at most 1000 characters after trimming."
10585
+ }
10586
+ }
10587
+ }
10588
+ }
10589
+ }
10590
+ },
10591
+ "responses": {
10592
+ "200": {
10593
+ "description": "A request was already pending: this is that request.",
10594
+ "content": {
10595
+ "application/json": {
10596
+ "schema": {
10597
+ "$ref": "#/components/schemas/AccountDeletionRequest"
10598
+ }
10599
+ }
10600
+ }
10601
+ },
10602
+ "202": {
10603
+ "description": "Request created.",
10604
+ "content": {
10605
+ "application/json": {
10606
+ "schema": {
10607
+ "$ref": "#/components/schemas/AccountDeletionRequest"
10608
+ }
10609
+ }
10610
+ }
10611
+ },
10612
+ "400": {
10613
+ "description": "Validation failure (a reason longer than 1000 characters).",
10614
+ "content": {
10615
+ "application/json": {
10616
+ "schema": {
10617
+ "$ref": "#/components/schemas/ErrorEnvelope"
10618
+ }
10619
+ }
10620
+ }
10621
+ },
10622
+ "401": {
10623
+ "description": "Not signed in.",
10624
+ "content": {
10625
+ "application/json": {
10626
+ "schema": {
10627
+ "$ref": "#/components/schemas/ErrorEnvelope"
10628
+ }
10629
+ }
10630
+ }
10631
+ },
10632
+ "403": {
10633
+ "description": "No organization on the session, or viewing the app as someone else.",
10634
+ "content": {
10635
+ "application/json": {
10636
+ "schema": {
10637
+ "$ref": "#/components/schemas/ErrorEnvelope"
10638
+ }
10639
+ }
10640
+ }
10641
+ },
10642
+ "426": {
10643
+ "description": "Client major below minClientVersion.",
10644
+ "content": {
10645
+ "application/json": {
10646
+ "schema": {
10647
+ "$ref": "#/components/schemas/UpgradeRequiredEnvelope"
10648
+ }
10649
+ }
10650
+ }
10651
+ },
10652
+ "429": {
10653
+ "description": "Too many requests from this person; try again later.",
10654
+ "content": {
10655
+ "application/json": {
10656
+ "schema": {
10657
+ "$ref": "#/components/schemas/ErrorEnvelope"
10658
+ }
10659
+ }
10660
+ }
10661
+ }
10662
+ }
10663
+ },
10664
+ "get": {
10665
+ "summary": "My newest account deletion request.",
10666
+ "tags": [
10667
+ "account"
10668
+ ],
10669
+ "parameters": [
10670
+ {
10671
+ "name": "X-Client-Version",
10672
+ "in": "header",
10673
+ "required": false,
10674
+ "schema": {
10675
+ "type": "string",
10676
+ "example": "1.0.0"
10677
+ },
10678
+ "description": "Semver of the calling app build. Drives the 426 / soft-upgrade handshake."
10679
+ }
10680
+ ],
10681
+ "responses": {
10682
+ "200": {
10683
+ "description": "The newest request, whatever its status.",
10684
+ "content": {
10685
+ "application/json": {
10686
+ "schema": {
10687
+ "$ref": "#/components/schemas/AccountDeletionRequest"
10688
+ }
10689
+ }
10690
+ }
10691
+ },
10692
+ "401": {
10693
+ "description": "Not signed in.",
10694
+ "content": {
10695
+ "application/json": {
10696
+ "schema": {
10697
+ "$ref": "#/components/schemas/ErrorEnvelope"
10698
+ }
10699
+ }
10700
+ }
10701
+ },
10702
+ "403": {
10703
+ "description": "No organization on the session.",
10704
+ "content": {
10705
+ "application/json": {
10706
+ "schema": {
10707
+ "$ref": "#/components/schemas/ErrorEnvelope"
10708
+ }
10709
+ }
10710
+ }
10711
+ },
10712
+ "404": {
10713
+ "description": "No request yet.",
10714
+ "content": {
10715
+ "application/json": {
10716
+ "schema": {
10717
+ "type": "object",
10718
+ "properties": {
10719
+ "message": {
10720
+ "type": "string",
10721
+ "example": "No deletion request"
10722
+ }
10723
+ },
10724
+ "required": [
10725
+ "message"
10726
+ ]
10727
+ }
10728
+ }
10729
+ }
10730
+ },
10731
+ "426": {
10732
+ "description": "Client major below minClientVersion.",
10733
+ "content": {
10734
+ "application/json": {
10735
+ "schema": {
10736
+ "$ref": "#/components/schemas/UpgradeRequiredEnvelope"
10737
+ }
10738
+ }
10739
+ }
10740
+ }
10741
+ }
10742
+ }
9993
10743
  }
9994
10744
  },
9995
10745
  "webhooks": {}