@medalsocial/sdk 1.7.0 → 1.9.0

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.
@@ -26,6 +26,11 @@ tags:
26
26
  Appointment bookings — the service catalogue, free slots, staff actions
27
27
  on a booking, and the customer-facing manage-token routes. Money is
28
28
  always integer øre.
29
+ - name: Portal
30
+ description: >-
31
+ Customer self-service portal — e-mail one-time-code login, then
32
+ session-bound access to the signed-in contact's own profile, bookings,
33
+ data export and erasure. Session routes carry `X-Portal-Session`.
29
34
  - name: GDPR
30
35
  description: Manage exports, consent records, and cookie consent.
31
36
  - name: Workspaces
@@ -727,6 +732,160 @@ paths:
727
732
  $ref: "#/components/schemas/ApiResponse_BookingScheduleDayArray"
728
733
  default:
729
734
  $ref: "#/components/responses/ApiError"
735
+ /api/v1/bookings/persons:
736
+ get:
737
+ tags: [Bookings]
738
+ operationId: listContactPersons
739
+ summary: List a contact's persons
740
+ description: >-
741
+ Persons a contact books for — a child, a pet, an employee. No login of
742
+ their own. Active-only unless `include_inactive` is set.
743
+ parameters:
744
+ - name: contact_id
745
+ in: query
746
+ required: true
747
+ schema:
748
+ type: string
749
+ - name: include_inactive
750
+ in: query
751
+ schema:
752
+ type: boolean
753
+ responses:
754
+ "200":
755
+ description: Contact persons.
756
+ content:
757
+ application/json:
758
+ schema:
759
+ $ref: "#/components/schemas/ApiResponse_ContactPersonArray"
760
+ default:
761
+ $ref: "#/components/responses/ApiError"
762
+ post:
763
+ tags: [Bookings]
764
+ operationId: createContactPerson
765
+ summary: Add a person under a contact
766
+ requestBody:
767
+ required: true
768
+ content:
769
+ application/json:
770
+ schema:
771
+ $ref: "#/components/schemas/CreateContactPersonInput"
772
+ responses:
773
+ "201":
774
+ description: Created person.
775
+ content:
776
+ application/json:
777
+ schema:
778
+ $ref: "#/components/schemas/ApiResponse_ContactPerson"
779
+ default:
780
+ $ref: "#/components/responses/ApiError"
781
+ /api/v1/bookings/relations:
782
+ get:
783
+ tags: [Bookings]
784
+ operationId: listContactRelations
785
+ summary: List a contact's relations
786
+ description: Directional relations a contact holds, split into outgoing and incoming.
787
+ parameters:
788
+ - name: contact_id
789
+ in: query
790
+ required: true
791
+ schema:
792
+ type: string
793
+ responses:
794
+ "200":
795
+ description: Outgoing and incoming relations.
796
+ content:
797
+ application/json:
798
+ schema:
799
+ $ref: "#/components/schemas/ApiResponse_ContactRelations"
800
+ default:
801
+ $ref: "#/components/responses/ApiError"
802
+ post:
803
+ tags: [Bookings]
804
+ operationId: createContactRelation
805
+ summary: Create a relation from one contact to another
806
+ requestBody:
807
+ required: true
808
+ content:
809
+ application/json:
810
+ schema:
811
+ $ref: "#/components/schemas/CreateContactRelationInput"
812
+ responses:
813
+ "201":
814
+ description: Created relation id.
815
+ content:
816
+ application/json:
817
+ schema:
818
+ $ref: "#/components/schemas/ApiResponse_CreateContactRelationResult"
819
+ default:
820
+ $ref: "#/components/responses/ApiError"
821
+ /api/v1/bookings/events:
822
+ get:
823
+ tags: [Bookings]
824
+ operationId: listBookingEvents
825
+ summary: List arrangementer in a date range
826
+ parameters:
827
+ - name: from
828
+ in: query
829
+ required: true
830
+ description: Start of the window, `yyyy-mm-dd`, inclusive.
831
+ schema:
832
+ type: string
833
+ format: date
834
+ - name: to
835
+ in: query
836
+ required: true
837
+ description: End of the window, `yyyy-mm-dd`, inclusive.
838
+ schema:
839
+ type: string
840
+ format: date
841
+ - name: status
842
+ in: query
843
+ schema:
844
+ $ref: "#/components/schemas/BookingEventStatus"
845
+ responses:
846
+ "200":
847
+ description: Arrangementer in the window.
848
+ content:
849
+ application/json:
850
+ schema:
851
+ $ref: "#/components/schemas/ApiResponse_BookingEventArray"
852
+ default:
853
+ $ref: "#/components/responses/ApiError"
854
+ post:
855
+ tags: [Bookings]
856
+ operationId: createBookingEvent
857
+ summary: Create an arrangement from a template
858
+ requestBody:
859
+ required: true
860
+ content:
861
+ application/json:
862
+ schema:
863
+ $ref: "#/components/schemas/CreateBookingEventInput"
864
+ responses:
865
+ "201":
866
+ description: Created arrangement.
867
+ content:
868
+ application/json:
869
+ schema:
870
+ $ref: "#/components/schemas/ApiResponse_BookingEvent"
871
+ default:
872
+ $ref: "#/components/responses/ApiError"
873
+ /api/v1/bookings/events/{id}:
874
+ parameters:
875
+ - $ref: "#/components/parameters/Id"
876
+ get:
877
+ tags: [Bookings]
878
+ operationId: getBookingEvent
879
+ summary: Get an arrangement
880
+ responses:
881
+ "200":
882
+ description: Arrangement.
883
+ content:
884
+ application/json:
885
+ schema:
886
+ $ref: "#/components/schemas/ApiResponse_BookingEvent"
887
+ default:
888
+ $ref: "#/components/responses/ApiError"
730
889
  /api/v1/bookings/{id}:
731
890
  parameters:
732
891
  - $ref: "#/components/parameters/Id"
@@ -898,6 +1057,188 @@ paths:
898
1057
  $ref: "#/components/schemas/ApiResponse_BookingRescheduleResult"
899
1058
  default:
900
1059
  $ref: "#/components/responses/ApiError"
1060
+ /api/v1/portal/login/start:
1061
+ post:
1062
+ tags: [Portal]
1063
+ operationId: startPortalLogin
1064
+ summary: E-mail a one-time login code
1065
+ description: >-
1066
+ Always answers 202 `{ status: "sent" }`, whether or not the address
1067
+ belongs to a contact — enumeration-safe by design. Rate-limited per
1068
+ address and per caller (`429 RATE_LIMITED`). Not idempotency-keyed.
1069
+ Errors: `400 VALIDATION_ERROR`, `403 FORBIDDEN` (key lacks
1070
+ `write:portal`), `429 RATE_LIMITED`.
1071
+ requestBody:
1072
+ required: true
1073
+ content:
1074
+ application/json:
1075
+ schema:
1076
+ $ref: "#/components/schemas/PortalLoginStartInput"
1077
+ responses:
1078
+ "202":
1079
+ description: Code queued (or silently not, for an unknown address).
1080
+ content:
1081
+ application/json:
1082
+ schema:
1083
+ $ref: "#/components/schemas/ApiResponse_PortalLoginStartResult"
1084
+ default:
1085
+ $ref: "#/components/responses/ApiError"
1086
+ /api/v1/portal/login/verify:
1087
+ post:
1088
+ tags: [Portal]
1089
+ operationId: verifyPortalLogin
1090
+ summary: Exchange the e-mailed code for a session
1091
+ description: >-
1092
+ A wrong, burned or expired code all answer `401 PORTAL_CODE_INVALID` —
1093
+ the three are not distinguished, so the response is not an oracle for
1094
+ which codes exist. The returned `session_token` is a bearer credential
1095
+ for ONE contact: keep it in an HttpOnly cookie on the site's server and
1096
+ never hand it to the browser. Not idempotency-keyed. Errors:
1097
+ `400 VALIDATION_ERROR`, `401 PORTAL_CODE_INVALID`, `403 FORBIDDEN`,
1098
+ `429 RATE_LIMITED`.
1099
+ requestBody:
1100
+ required: true
1101
+ content:
1102
+ application/json:
1103
+ schema:
1104
+ $ref: "#/components/schemas/PortalVerifyInput"
1105
+ responses:
1106
+ "200":
1107
+ description: The new session.
1108
+ content:
1109
+ application/json:
1110
+ schema:
1111
+ $ref: "#/components/schemas/ApiResponse_PortalSession"
1112
+ default:
1113
+ $ref: "#/components/responses/ApiError"
1114
+ /api/v1/portal/logout:
1115
+ parameters:
1116
+ - $ref: "#/components/parameters/PortalSession"
1117
+ post:
1118
+ tags: [Portal]
1119
+ operationId: logoutPortal
1120
+ summary: Revoke the session
1121
+ description: >-
1122
+ Revoking twice reaches the same state — the second call answers
1123
+ `401 PORTAL_SESSION_INVALID`. Not idempotency-keyed. Errors:
1124
+ `401 PORTAL_SESSION_REQUIRED` (header missing),
1125
+ `401 PORTAL_SESSION_INVALID` (unknown, expired or revoked),
1126
+ `403 FORBIDDEN`, `429 RATE_LIMITED`.
1127
+ responses:
1128
+ "204":
1129
+ description: Session revoked.
1130
+ default:
1131
+ $ref: "#/components/responses/ApiError"
1132
+ /api/v1/portal/me:
1133
+ parameters:
1134
+ - $ref: "#/components/parameters/PortalSession"
1135
+ get:
1136
+ tags: [Portal]
1137
+ operationId: getPortalProfile
1138
+ summary: The signed-in contact's profile
1139
+ description: >-
1140
+ Errors: `401 PORTAL_SESSION_REQUIRED`, `401 PORTAL_SESSION_INVALID`,
1141
+ `403 FORBIDDEN` (key lacks `read:portal`), `429 RATE_LIMITED`.
1142
+ responses:
1143
+ "200":
1144
+ description: Profile.
1145
+ content:
1146
+ application/json:
1147
+ schema:
1148
+ $ref: "#/components/schemas/ApiResponse_PortalProfile"
1149
+ default:
1150
+ $ref: "#/components/responses/ApiError"
1151
+ patch:
1152
+ tags: [Portal]
1153
+ operationId: updatePortalProfile
1154
+ summary: Update the signed-in contact's profile
1155
+ description: >-
1156
+ Only the supplied fields change. `phone: null` clears the number,
1157
+ `family` replaces the whole list, and `marketing_consent` records a
1158
+ `marketing_email` consent decision with source `portal`. Answers the
1159
+ profile as it is after the change. Errors: `400 VALIDATION_ERROR`,
1160
+ `401 PORTAL_SESSION_REQUIRED`, `401 PORTAL_SESSION_INVALID`,
1161
+ `403 FORBIDDEN` (key lacks `write:portal`), `429 RATE_LIMITED`.
1162
+ requestBody:
1163
+ required: true
1164
+ content:
1165
+ application/json:
1166
+ schema:
1167
+ $ref: "#/components/schemas/PortalProfilePatch"
1168
+ responses:
1169
+ "200":
1170
+ description: Updated profile.
1171
+ content:
1172
+ application/json:
1173
+ schema:
1174
+ $ref: "#/components/schemas/ApiResponse_PortalProfile"
1175
+ default:
1176
+ $ref: "#/components/responses/ApiError"
1177
+ /api/v1/portal/me/bookings:
1178
+ parameters:
1179
+ - $ref: "#/components/parameters/PortalSession"
1180
+ get:
1181
+ tags: [Portal]
1182
+ operationId: listPortalBookings
1183
+ summary: The signed-in contact's bookings
1184
+ description: >-
1185
+ Split into `upcoming` and `past`. An upcoming booking still inside the
1186
+ workspace's policy windows carries `manage_token` and
1187
+ `can_manage: true`; the token opens the site's manage page. Errors:
1188
+ `401 PORTAL_SESSION_REQUIRED`, `401 PORTAL_SESSION_INVALID`,
1189
+ `403 FORBIDDEN`, `429 RATE_LIMITED`.
1190
+ responses:
1191
+ "200":
1192
+ description: Bookings.
1193
+ content:
1194
+ application/json:
1195
+ schema:
1196
+ $ref: "#/components/schemas/ApiResponse_PortalBookings"
1197
+ default:
1198
+ $ref: "#/components/responses/ApiError"
1199
+ /api/v1/portal/me/export:
1200
+ parameters:
1201
+ - $ref: "#/components/parameters/PortalSession"
1202
+ post:
1203
+ tags: [Portal]
1204
+ operationId: exportPortalData
1205
+ summary: Export everything held about the signed-in contact
1206
+ description: >-
1207
+ A synchronous GDPR Art. 15 export of the contact's profile, family,
1208
+ consents and bookings, as one JSON document. Unlike `GET
1209
+ /api/v1/portal/me`, `contact.persons` here includes inactive persons
1210
+ so the export is a complete dump. `relations` lists every relation
1211
+ the contact is a party to, exposing only the counterpart's display
1212
+ name. Read-only, so not idempotency-keyed. Errors: `401
1213
+ PORTAL_SESSION_REQUIRED`, `401 PORTAL_SESSION_INVALID`, `403
1214
+ FORBIDDEN`, `429 RATE_LIMITED`.
1215
+ responses:
1216
+ "200":
1217
+ description: The export.
1218
+ content:
1219
+ application/json:
1220
+ schema:
1221
+ $ref: "#/components/schemas/ApiResponse_PortalExport"
1222
+ default:
1223
+ $ref: "#/components/responses/ApiError"
1224
+ /api/v1/portal/me/delete:
1225
+ parameters:
1226
+ - $ref: "#/components/parameters/PortalSession"
1227
+ post:
1228
+ tags: [Portal]
1229
+ operationId: deletePortalAccount
1230
+ summary: Erase the signed-in contact
1231
+ description: >-
1232
+ GDPR Art. 17 erasure. The session is revoked as part of the deletion,
1233
+ so a retry answers `401 PORTAL_SESSION_INVALID` rather than deleting
1234
+ anything else. Not idempotency-keyed. Errors:
1235
+ `401 PORTAL_SESSION_REQUIRED`, `401 PORTAL_SESSION_INVALID`,
1236
+ `403 FORBIDDEN` (key lacks `write:portal`), `429 RATE_LIMITED`.
1237
+ responses:
1238
+ "204":
1239
+ description: Contact erased and session revoked.
1240
+ default:
1241
+ $ref: "#/components/responses/ApiError"
901
1242
  /api/v1/gdpr/export:
902
1243
  post:
903
1244
  tags: [GDPR]
@@ -1541,6 +1882,18 @@ components:
1541
1882
  Possession of it authorizes the customer's own cancel or reschedule.
1542
1883
  schema:
1543
1884
  type: string
1885
+ PortalSession:
1886
+ name: X-Portal-Session
1887
+ in: header
1888
+ required: true
1889
+ description: >-
1890
+ The `session_token` returned by `verifyPortalLogin`. A bearer
1891
+ credential for ONE contact — the site's server keeps it in an HttpOnly
1892
+ cookie and forwards it here. Missing answers
1893
+ `401 PORTAL_SESSION_REQUIRED`; unknown, expired or revoked answers
1894
+ `401 PORTAL_SESSION_INVALID`.
1895
+ schema:
1896
+ type: string
1544
1897
  Cursor:
1545
1898
  name: cursor
1546
1899
  in: query
@@ -2449,6 +2802,9 @@ components:
2449
2802
  - end_ts
2450
2803
  - booked_for_name
2451
2804
  - booked_for_birth_year
2805
+ - booked_for_person_id
2806
+ - event_id
2807
+ - event_order
2452
2808
  - party_sequence_id
2453
2809
  - status
2454
2810
  - cancelled_by
@@ -2481,6 +2837,15 @@ components:
2481
2837
  booked_for_birth_year:
2482
2838
  description: A birth year, not a birthdate — the age bracket is all that is stored.
2483
2839
  type: [integer, "null"]
2840
+ booked_for_person_id:
2841
+ description: The ContactPerson this booking was made for, if any.
2842
+ type: [string, "null"]
2843
+ event_id:
2844
+ description: The BookingEvent this booking is a registration for, if any.
2845
+ type: [string, "null"]
2846
+ event_order:
2847
+ description: Position of this booking within its event's registrations, if any.
2848
+ type: [integer, "null"]
2484
2849
  party_sequence_id:
2485
2850
  description: Shared by every booking created in the same party request.
2486
2851
  type: [string, "null"]
@@ -2685,6 +3050,9 @@ components:
2685
3050
  type: integer
2686
3051
  minimum: 1900
2687
3052
  maximum: 2200
3053
+ booked_for_person_id:
3054
+ description: Book this line on behalf of a ContactPerson rather than the contact.
3055
+ type: string
2688
3056
  CreateBookingInput:
2689
3057
  type: object
2690
3058
  required: [items, contact]
@@ -3615,6 +3983,16 @@ components:
3615
3983
  $ref: "#/components/schemas/Envelope_BookingRescheduleResult"
3616
3984
  ApiResponse_ManageSummary:
3617
3985
  $ref: "#/components/schemas/Envelope_ManageSummary"
3986
+ ApiResponse_PortalLoginStartResult:
3987
+ $ref: "#/components/schemas/Envelope_PortalLoginStartResult"
3988
+ ApiResponse_PortalSession:
3989
+ $ref: "#/components/schemas/Envelope_PortalSession"
3990
+ ApiResponse_PortalProfile:
3991
+ $ref: "#/components/schemas/Envelope_PortalProfile"
3992
+ ApiResponse_PortalBookings:
3993
+ $ref: "#/components/schemas/Envelope_PortalBookings"
3994
+ ApiResponse_PortalExport:
3995
+ $ref: "#/components/schemas/Envelope_PortalExport"
3618
3996
  ApiResponse_GdprExportRequest:
3619
3997
  $ref: "#/components/schemas/Envelope_GdprExportRequest"
3620
3998
  ApiResponse_GdprExportArray:
@@ -3946,64 +4324,377 @@ components:
3946
4324
  properties:
3947
4325
  data:
3948
4326
  $ref: "#/components/schemas/ManageSummary"
3949
- ScanCreateInput:
4327
+ PortalLoginStartInput:
3950
4328
  type: object
3951
- description: Provide exactly one of `url`, `orgnr`, or `name`.
3952
- oneOf:
3953
- - required: [url]
3954
- not:
3955
- anyOf: [{ required: [orgnr] }, { required: [name] }]
3956
- - required: [orgnr]
3957
- not:
3958
- anyOf: [{ required: [url] }, { required: [name] }]
3959
- - required: [name]
3960
- not:
3961
- anyOf: [{ required: [url] }, { required: [orgnr] }]
4329
+ required: [email]
3962
4330
  properties:
3963
- url:
3964
- type: string
3965
- minLength: 4
3966
- maxLength: 500
3967
- orgnr:
4331
+ email:
3968
4332
  type: string
3969
- pattern: "^\\d{9}$"
3970
- name:
4333
+ format: email
4334
+ description: The address the code is sent to.
4335
+ locale:
3971
4336
  type: string
3972
- minLength: 2
3973
- maxLength: 120
3974
- ScanCreateResult:
4337
+ description: Locale for the e-mail (e.g. `nb`, `en`); the workspace default when omitted.
4338
+ PortalLoginStartResult:
4339
+ description: >-
4340
+ Always `sent`, whether or not the address is a known contact — the
4341
+ route is enumeration-safe by design.
3975
4342
  type: object
3976
- required: [id, status]
4343
+ required: [status]
3977
4344
  properties:
3978
- id:
3979
- type: string
3980
4345
  status:
3981
4346
  type: string
3982
- message:
3983
- type: string
3984
- ScanCompany:
4347
+ const: sent
4348
+ PortalVerifyInput:
3985
4349
  type: object
3986
- required: [orgnr, name, org_form, industry, city, website]
4350
+ required: [email, code]
3987
4351
  properties:
3988
- orgnr:
4352
+ email:
3989
4353
  type: string
3990
- name:
4354
+ format: email
4355
+ code:
3991
4356
  type: string
3992
- org_form:
3993
- type: [string, "null"]
3994
- industry:
3995
- type: [string, "null"]
3996
- city:
3997
- type: [string, "null"]
3998
- website:
3999
- type: [string, "null"]
4000
- ScanJob:
4357
+ description: The one-time code from the e-mail.
4358
+ PortalContactSummary:
4001
4359
  type: object
4002
- required: [id, status, input, resolved, result, error, created_at, finished_at]
4360
+ required: [contact_id, first_name]
4003
4361
  properties:
4004
- id:
4362
+ contact_id:
4005
4363
  type: string
4006
- status:
4364
+ first_name:
4365
+ type: [string, "null"]
4366
+ PortalSession:
4367
+ description: >-
4368
+ A portal session. `session_token` is a bearer credential for ONE
4369
+ contact — keep it in an HttpOnly cookie on the site's server and never
4370
+ hand it to the browser.
4371
+ type: object
4372
+ required: [session_token, expires_at, contact]
4373
+ properties:
4374
+ session_token:
4375
+ type: string
4376
+ expires_at:
4377
+ type: integer
4378
+ description: Unix timestamp in milliseconds.
4379
+ contact:
4380
+ $ref: "#/components/schemas/PortalContactSummary"
4381
+ PortalFamilyMember:
4382
+ type: object
4383
+ required: [name, birth_year]
4384
+ properties:
4385
+ name:
4386
+ type: string
4387
+ birth_year:
4388
+ type: integer
4389
+ PortalPerson:
4390
+ description: >-
4391
+ A person the contact books for — a child, a pet — with no login of
4392
+ its own.
4393
+ type: object
4394
+ required:
4395
+ [person_id, name, birth_year, relation_type, relation_label, notes, active]
4396
+ properties:
4397
+ person_id:
4398
+ type: string
4399
+ name:
4400
+ type: string
4401
+ birth_year:
4402
+ type: [integer, "null"]
4403
+ relation_type:
4404
+ $ref: "#/components/schemas/RelationType"
4405
+ relation_label:
4406
+ type: [string, "null"]
4407
+ notes:
4408
+ type: [string, "null"]
4409
+ active:
4410
+ type: boolean
4411
+ description: >-
4412
+ False for a person the customer removed or that was promoted to
4413
+ its own contact; `me` returns active persons only, the export
4414
+ returns all.
4415
+ PortalLabels:
4416
+ description: The workspace's own words for the person concept, e.g. `"Barn"`.
4417
+ type: object
4418
+ required: [person, persons]
4419
+ properties:
4420
+ person:
4421
+ type: string
4422
+ persons:
4423
+ type: string
4424
+ PortalProfile:
4425
+ type: object
4426
+ required:
4427
+ - contact_id
4428
+ - email
4429
+ - first_name
4430
+ - last_name
4431
+ - phone
4432
+ - family
4433
+ - persons
4434
+ - labels
4435
+ - marketing_consent
4436
+ - created_at
4437
+ properties:
4438
+ contact_id:
4439
+ type: string
4440
+ email:
4441
+ type: string
4442
+ first_name:
4443
+ type: [string, "null"]
4444
+ last_name:
4445
+ type: [string, "null"]
4446
+ phone:
4447
+ type: [string, "null"]
4448
+ family:
4449
+ type: array
4450
+ items:
4451
+ $ref: "#/components/schemas/PortalFamilyMember"
4452
+ persons:
4453
+ type: array
4454
+ items:
4455
+ $ref: "#/components/schemas/PortalPerson"
4456
+ labels:
4457
+ $ref: "#/components/schemas/PortalLabels"
4458
+ marketing_consent:
4459
+ type: boolean
4460
+ created_at:
4461
+ type: integer
4462
+ description: Unix timestamp in milliseconds.
4463
+ PortalProfilePatch:
4464
+ description: Only the supplied fields change.
4465
+ type: object
4466
+ properties:
4467
+ first_name:
4468
+ type: string
4469
+ last_name:
4470
+ type: string
4471
+ phone:
4472
+ type: [string, "null"]
4473
+ description: "`null` clears the number."
4474
+ family:
4475
+ type: array
4476
+ description: Replaces the whole list.
4477
+ items:
4478
+ $ref: "#/components/schemas/PortalFamilyMember"
4479
+ marketing_consent:
4480
+ type: boolean
4481
+ description: Records a `marketing_email` consent change with source `portal`.
4482
+ PortalBooking:
4483
+ type: object
4484
+ required:
4485
+ - booking_id
4486
+ - status
4487
+ - start_ts
4488
+ - end_ts
4489
+ - service_id
4490
+ - service_name
4491
+ - resource_id
4492
+ - resource_name
4493
+ - booked_for_name
4494
+ - amount_ore
4495
+ - notes
4496
+ - manage_token
4497
+ - can_manage
4498
+ properties:
4499
+ booking_id:
4500
+ type: string
4501
+ status:
4502
+ $ref: "#/components/schemas/BookingStatus"
4503
+ start_ts:
4504
+ type: integer
4505
+ description: Unix timestamp in milliseconds.
4506
+ end_ts:
4507
+ type: integer
4508
+ description: Unix timestamp in milliseconds.
4509
+ service_id:
4510
+ type: [string, "null"]
4511
+ service_name:
4512
+ type: [string, "null"]
4513
+ resource_id:
4514
+ type: [string, "null"]
4515
+ resource_name:
4516
+ type: [string, "null"]
4517
+ booked_for_name:
4518
+ type: [string, "null"]
4519
+ amount_ore:
4520
+ type: [integer, "null"]
4521
+ description: Integer øre, or `null` when the service has no price.
4522
+ notes:
4523
+ type: [string, "null"]
4524
+ manage_token:
4525
+ type: [string, "null"]
4526
+ description: >-
4527
+ Present only while the booking is upcoming and manageable; opens
4528
+ the site's manage page.
4529
+ can_manage:
4530
+ type: boolean
4531
+ PortalBookings:
4532
+ type: object
4533
+ required: [upcoming, past]
4534
+ properties:
4535
+ upcoming:
4536
+ type: array
4537
+ items:
4538
+ $ref: "#/components/schemas/PortalBooking"
4539
+ past:
4540
+ type: array
4541
+ items:
4542
+ $ref: "#/components/schemas/PortalBooking"
4543
+ PortalConsentRecord:
4544
+ type: object
4545
+ required: [consent_type, granted, granted_at, revoked_at, source]
4546
+ properties:
4547
+ consent_type:
4548
+ type: string
4549
+ granted:
4550
+ type: boolean
4551
+ granted_at:
4552
+ type: [integer, "null"]
4553
+ description: Unix timestamp in milliseconds, or `null`.
4554
+ revoked_at:
4555
+ type: [integer, "null"]
4556
+ description: Unix timestamp in milliseconds, or `null`.
4557
+ source:
4558
+ type: string
4559
+ PortalExportRelation:
4560
+ description: A relation the exporting contact is a party to; only the counterpart's display name is exposed.
4561
+ type: object
4562
+ required:
4563
+ - direction
4564
+ - type
4565
+ - custom_label
4566
+ - since
4567
+ - note
4568
+ - counterpart_name
4569
+ properties:
4570
+ direction:
4571
+ type: string
4572
+ enum: [outgoing, incoming]
4573
+ type:
4574
+ $ref: "#/components/schemas/RelationType"
4575
+ custom_label:
4576
+ type: [string, "null"]
4577
+ since:
4578
+ type: [integer, "null"]
4579
+ description: Unix timestamp in milliseconds.
4580
+ note:
4581
+ type: [string, "null"]
4582
+ counterpart_name:
4583
+ type: string
4584
+ PortalExport:
4585
+ description: Everything the workspace holds about the signed-in contact (GDPR Art. 15).
4586
+ type: object
4587
+ required: [exported_at, contact, family, consents, bookings, relations]
4588
+ properties:
4589
+ exported_at:
4590
+ type: integer
4591
+ description: Unix timestamp in milliseconds.
4592
+ contact:
4593
+ $ref: "#/components/schemas/PortalProfile"
4594
+ family:
4595
+ type: array
4596
+ items:
4597
+ $ref: "#/components/schemas/PortalFamilyMember"
4598
+ consents:
4599
+ type: array
4600
+ items:
4601
+ $ref: "#/components/schemas/PortalConsentRecord"
4602
+ bookings:
4603
+ type: array
4604
+ items:
4605
+ $ref: "#/components/schemas/PortalBooking"
4606
+ relations:
4607
+ type: array
4608
+ items:
4609
+ $ref: "#/components/schemas/PortalExportRelation"
4610
+ Envelope_PortalLoginStartResult:
4611
+ type: object
4612
+ required: [data]
4613
+ properties:
4614
+ data:
4615
+ $ref: "#/components/schemas/PortalLoginStartResult"
4616
+ Envelope_PortalSession:
4617
+ type: object
4618
+ required: [data]
4619
+ properties:
4620
+ data:
4621
+ $ref: "#/components/schemas/PortalSession"
4622
+ Envelope_PortalProfile:
4623
+ type: object
4624
+ required: [data]
4625
+ properties:
4626
+ data:
4627
+ $ref: "#/components/schemas/PortalProfile"
4628
+ Envelope_PortalBookings:
4629
+ type: object
4630
+ required: [data]
4631
+ properties:
4632
+ data:
4633
+ $ref: "#/components/schemas/PortalBookings"
4634
+ Envelope_PortalExport:
4635
+ type: object
4636
+ required: [data]
4637
+ properties:
4638
+ data:
4639
+ $ref: "#/components/schemas/PortalExport"
4640
+ ScanCreateInput:
4641
+ type: object
4642
+ description: Provide exactly one of `url`, `orgnr`, or `name`.
4643
+ oneOf:
4644
+ - required: [url]
4645
+ not:
4646
+ anyOf: [{ required: [orgnr] }, { required: [name] }]
4647
+ - required: [orgnr]
4648
+ not:
4649
+ anyOf: [{ required: [url] }, { required: [name] }]
4650
+ - required: [name]
4651
+ not:
4652
+ anyOf: [{ required: [url] }, { required: [orgnr] }]
4653
+ properties:
4654
+ url:
4655
+ type: string
4656
+ minLength: 4
4657
+ maxLength: 500
4658
+ orgnr:
4659
+ type: string
4660
+ pattern: "^\\d{9}$"
4661
+ name:
4662
+ type: string
4663
+ minLength: 2
4664
+ maxLength: 120
4665
+ ScanCreateResult:
4666
+ type: object
4667
+ required: [id, status]
4668
+ properties:
4669
+ id:
4670
+ type: string
4671
+ status:
4672
+ type: string
4673
+ message:
4674
+ type: string
4675
+ ScanCompany:
4676
+ type: object
4677
+ required: [orgnr, name, org_form, industry, city, website]
4678
+ properties:
4679
+ orgnr:
4680
+ type: string
4681
+ name:
4682
+ type: string
4683
+ org_form:
4684
+ type: [string, "null"]
4685
+ industry:
4686
+ type: [string, "null"]
4687
+ city:
4688
+ type: [string, "null"]
4689
+ website:
4690
+ type: [string, "null"]
4691
+ ScanJob:
4692
+ type: object
4693
+ required: [id, status, input, resolved, result, error, created_at, finished_at]
4694
+ properties:
4695
+ id:
4696
+ type: string
4697
+ status:
4007
4698
  type: string
4008
4699
  description: pending | running | done | failed
4009
4700
  input:
@@ -4297,3 +4988,293 @@ components:
4297
4988
  properties:
4298
4989
  data:
4299
4990
  $ref: "#/components/schemas/ChannelConnectionDisconnectResult"
4991
+ RelationType:
4992
+ description: How a person or a relation relates to a contact.
4993
+ type: string
4994
+ enum: [guardian, owner, employer, caregiver, partner, custom]
4995
+ ContactPerson:
4996
+ description: >-
4997
+ A person a contact books for — a child, a pet, an employee. No login
4998
+ of their own.
4999
+ type: object
5000
+ required:
5001
+ - person_id
5002
+ - contact_id
5003
+ - name
5004
+ - birth_year
5005
+ - relation_type
5006
+ - relation_label
5007
+ - notes
5008
+ - active
5009
+ - promoted_to_contact_id
5010
+ - created_at
5011
+ - updated_at
5012
+ properties:
5013
+ person_id:
5014
+ type: string
5015
+ contact_id:
5016
+ type: string
5017
+ name:
5018
+ type: string
5019
+ birth_year:
5020
+ type: [integer, "null"]
5021
+ relation_type:
5022
+ $ref: "#/components/schemas/RelationType"
5023
+ relation_label:
5024
+ type: [string, "null"]
5025
+ notes:
5026
+ type: [string, "null"]
5027
+ active:
5028
+ type: boolean
5029
+ promoted_to_contact_id:
5030
+ type: [string, "null"]
5031
+ created_at:
5032
+ type: string
5033
+ format: date-time
5034
+ updated_at:
5035
+ type: string
5036
+ format: date-time
5037
+ CreateContactPersonInput:
5038
+ type: object
5039
+ required: [contact_id, name, relation_type]
5040
+ properties:
5041
+ contact_id:
5042
+ type: string
5043
+ name:
5044
+ type: string
5045
+ birth_year:
5046
+ type: integer
5047
+ relation_type:
5048
+ $ref: "#/components/schemas/RelationType"
5049
+ relation_label:
5050
+ type: string
5051
+ notes:
5052
+ type: string
5053
+ ContactRelation:
5054
+ description: One directional relation between two contacts.
5055
+ type: object
5056
+ required:
5057
+ - relation_id
5058
+ - from_contact_id
5059
+ - to_contact_id
5060
+ - type
5061
+ - custom_label
5062
+ - since
5063
+ - note
5064
+ - counterpart_name
5065
+ - created_at
5066
+ properties:
5067
+ relation_id:
5068
+ type: string
5069
+ from_contact_id:
5070
+ type: string
5071
+ to_contact_id:
5072
+ type: string
5073
+ type:
5074
+ $ref: "#/components/schemas/RelationType"
5075
+ custom_label:
5076
+ type: [string, "null"]
5077
+ since:
5078
+ type: [integer, "null"]
5079
+ description: Unix timestamp in milliseconds.
5080
+ note:
5081
+ type: [string, "null"]
5082
+ counterpart_name:
5083
+ type: string
5084
+ description: Empty string when the counterpart contact no longer exists.
5085
+ created_at:
5086
+ type: string
5087
+ format: date-time
5088
+ ContactRelations:
5089
+ description: Relations a contact holds, split by direction.
5090
+ type: object
5091
+ required: [outgoing, incoming]
5092
+ properties:
5093
+ outgoing:
5094
+ type: array
5095
+ items:
5096
+ $ref: "#/components/schemas/ContactRelation"
5097
+ incoming:
5098
+ type: array
5099
+ items:
5100
+ $ref: "#/components/schemas/ContactRelation"
5101
+ CreateContactRelationInput:
5102
+ type: object
5103
+ required: [from_contact_id, to_contact_id, type]
5104
+ properties:
5105
+ from_contact_id:
5106
+ type: string
5107
+ to_contact_id:
5108
+ type: string
5109
+ type:
5110
+ $ref: "#/components/schemas/RelationType"
5111
+ custom_label:
5112
+ type: string
5113
+ since:
5114
+ type: integer
5115
+ description: Unix timestamp in milliseconds.
5116
+ note:
5117
+ type: string
5118
+ CreateContactRelationResult:
5119
+ type: object
5120
+ required: [relation_id]
5121
+ properties:
5122
+ relation_id:
5123
+ type: string
5124
+ BookingEventStatus:
5125
+ type: string
5126
+ enum: [draft, open, closed, completed, cancelled]
5127
+ BookingEventTemplateKey:
5128
+ type: string
5129
+ enum: [kindergarten_visit, company_day, class, open_day, custom]
5130
+ BookingEvent:
5131
+ description: An arrangement — a scheduled group session bookings register against.
5132
+ type: object
5133
+ required:
5134
+ - event_id
5135
+ - template_id
5136
+ - host_id
5137
+ - date
5138
+ - window_start_minute
5139
+ - window_end_minute
5140
+ - place
5141
+ - capacity
5142
+ - minimum
5143
+ - registered_count
5144
+ - service_ids
5145
+ - resource_ids
5146
+ - price_override_ore
5147
+ - status
5148
+ - registration_closes_at
5149
+ - slug
5150
+ - created_at
5151
+ - updated_at
5152
+ properties:
5153
+ event_id:
5154
+ type: string
5155
+ template_id:
5156
+ type: string
5157
+ host_id:
5158
+ type: [string, "null"]
5159
+ date:
5160
+ description: yyyy-mm-dd in the workspace time zone.
5161
+ type: string
5162
+ format: date
5163
+ window_start_minute:
5164
+ type: integer
5165
+ window_end_minute:
5166
+ type: integer
5167
+ place:
5168
+ type: string
5169
+ enum: [at_host, in_house]
5170
+ capacity:
5171
+ type: integer
5172
+ minimum:
5173
+ type: integer
5174
+ registered_count:
5175
+ type: integer
5176
+ service_ids:
5177
+ type: array
5178
+ items:
5179
+ type: string
5180
+ resource_ids:
5181
+ type: array
5182
+ items:
5183
+ type: string
5184
+ price_override_ore:
5185
+ type: [integer, "null"]
5186
+ status:
5187
+ $ref: "#/components/schemas/BookingEventStatus"
5188
+ registration_closes_at:
5189
+ type: string
5190
+ format: date-time
5191
+ slug:
5192
+ type: string
5193
+ created_at:
5194
+ type: string
5195
+ format: date-time
5196
+ updated_at:
5197
+ type: string
5198
+ format: date-time
5199
+ CreateBookingEventInput:
5200
+ type: object
5201
+ required: [template_key, date, window_start_minute, place, service_ids, resource_ids]
5202
+ properties:
5203
+ template_key:
5204
+ $ref: "#/components/schemas/BookingEventTemplateKey"
5205
+ host_id:
5206
+ type: string
5207
+ date:
5208
+ type: string
5209
+ format: date
5210
+ window_start_minute:
5211
+ type: integer
5212
+ window_end_minute:
5213
+ type: integer
5214
+ place:
5215
+ type: string
5216
+ enum: [at_host, in_house]
5217
+ capacity:
5218
+ type: integer
5219
+ minimum:
5220
+ type: integer
5221
+ service_ids:
5222
+ type: array
5223
+ items:
5224
+ type: string
5225
+ resource_ids:
5226
+ type: array
5227
+ items:
5228
+ type: string
5229
+ ApiResponse_ContactPersonArray:
5230
+ $ref: "#/components/schemas/Envelope_ContactPersonArray"
5231
+ ApiResponse_ContactPerson:
5232
+ $ref: "#/components/schemas/Envelope_ContactPerson"
5233
+ ApiResponse_ContactRelations:
5234
+ $ref: "#/components/schemas/Envelope_ContactRelations"
5235
+ ApiResponse_CreateContactRelationResult:
5236
+ $ref: "#/components/schemas/Envelope_CreateContactRelationResult"
5237
+ ApiResponse_BookingEventArray:
5238
+ $ref: "#/components/schemas/Envelope_BookingEventArray"
5239
+ ApiResponse_BookingEvent:
5240
+ $ref: "#/components/schemas/Envelope_BookingEvent"
5241
+ Envelope_ContactPersonArray:
5242
+ type: object
5243
+ required: [data]
5244
+ properties:
5245
+ data:
5246
+ type: array
5247
+ items:
5248
+ $ref: "#/components/schemas/ContactPerson"
5249
+ Envelope_ContactPerson:
5250
+ type: object
5251
+ required: [data]
5252
+ properties:
5253
+ data:
5254
+ $ref: "#/components/schemas/ContactPerson"
5255
+ Envelope_ContactRelations:
5256
+ type: object
5257
+ required: [data]
5258
+ properties:
5259
+ data:
5260
+ $ref: "#/components/schemas/ContactRelations"
5261
+ Envelope_CreateContactRelationResult:
5262
+ type: object
5263
+ required: [data]
5264
+ properties:
5265
+ data:
5266
+ $ref: "#/components/schemas/CreateContactRelationResult"
5267
+ Envelope_BookingEventArray:
5268
+ type: object
5269
+ required: [data]
5270
+ properties:
5271
+ data:
5272
+ type: array
5273
+ items:
5274
+ $ref: "#/components/schemas/BookingEvent"
5275
+ Envelope_BookingEvent:
5276
+ type: object
5277
+ required: [data]
5278
+ properties:
5279
+ data:
5280
+ $ref: "#/components/schemas/BookingEvent"