@medalsocial/sdk 1.6.0 → 1.8.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.
- package/README.md +106 -0
- package/dist/openapi/medal-social.openapi.json +4783 -2489
- package/dist/src/index.d.mts +822 -25
- package/dist/src/index.d.ts +822 -25
- package/dist/src/index.js +607 -156
- package/dist/src/index.js.map +1 -1
- package/dist/src/index.mjs +605 -156
- package/dist/src/index.mjs.map +1 -1
- package/dist/src/openapi.generated.d.mts +1892 -559
- package/dist/src/openapi.generated.d.ts +1892 -559
- package/dist/src/openapi.generated.js.map +1 -1
- package/openapi/medal-social.openapi.yaml +1402 -12
- package/package.json +18 -17
- package/skills/client/SKILL.md +4 -2
- package/skills/resources/SKILL.md +152 -3
|
@@ -21,6 +21,16 @@ tags:
|
|
|
21
21
|
description: Manage workspace CRM contacts.
|
|
22
22
|
- name: Deals
|
|
23
23
|
description: Manage sponsorship and brand deals.
|
|
24
|
+
- name: Bookings
|
|
25
|
+
description: >-
|
|
26
|
+
Appointment bookings — the service catalogue, free slots, staff actions
|
|
27
|
+
on a booking, and the customer-facing manage-token routes. Money is
|
|
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`.
|
|
24
34
|
- name: GDPR
|
|
25
35
|
description: Manage exports, consent records, and cookie consent.
|
|
26
36
|
- name: Workspaces
|
|
@@ -543,6 +553,534 @@ paths:
|
|
|
543
553
|
$ref: "#/components/schemas/ApiResponse_DealRemoveResult"
|
|
544
554
|
default:
|
|
545
555
|
$ref: "#/components/responses/ApiError"
|
|
556
|
+
/api/v1/bookings:
|
|
557
|
+
get:
|
|
558
|
+
tags: [Bookings]
|
|
559
|
+
operationId: listBookings
|
|
560
|
+
summary: List bookings
|
|
561
|
+
parameters:
|
|
562
|
+
- $ref: "#/components/parameters/Limit"
|
|
563
|
+
- $ref: "#/components/parameters/Cursor"
|
|
564
|
+
- name: from_ts
|
|
565
|
+
in: query
|
|
566
|
+
description: Only bookings starting at or after this instant.
|
|
567
|
+
schema:
|
|
568
|
+
$ref: "#/components/schemas/BookingTimestampInput"
|
|
569
|
+
- name: to_ts
|
|
570
|
+
in: query
|
|
571
|
+
description: Only bookings starting before this instant.
|
|
572
|
+
schema:
|
|
573
|
+
$ref: "#/components/schemas/BookingTimestampInput"
|
|
574
|
+
- name: status
|
|
575
|
+
in: query
|
|
576
|
+
schema:
|
|
577
|
+
$ref: "#/components/schemas/BookingStatus"
|
|
578
|
+
- name: resource_id
|
|
579
|
+
in: query
|
|
580
|
+
schema:
|
|
581
|
+
type: string
|
|
582
|
+
responses:
|
|
583
|
+
"200":
|
|
584
|
+
description: Bookings page.
|
|
585
|
+
content:
|
|
586
|
+
application/json:
|
|
587
|
+
schema:
|
|
588
|
+
$ref: "#/components/schemas/PaginatedResponse_Booking"
|
|
589
|
+
default:
|
|
590
|
+
$ref: "#/components/responses/ApiError"
|
|
591
|
+
post:
|
|
592
|
+
tags: [Bookings]
|
|
593
|
+
operationId: createBooking
|
|
594
|
+
summary: Create a booking or party
|
|
595
|
+
description: >-
|
|
596
|
+
Books every item or none of them. Each created booking carries a
|
|
597
|
+
`manage_token` returned exactly once — only its hash is stored, and an
|
|
598
|
+
idempotent replay omits it.
|
|
599
|
+
requestBody:
|
|
600
|
+
required: true
|
|
601
|
+
content:
|
|
602
|
+
application/json:
|
|
603
|
+
schema:
|
|
604
|
+
$ref: "#/components/schemas/CreateBookingInput"
|
|
605
|
+
responses:
|
|
606
|
+
"201":
|
|
607
|
+
description: Created bookings, in request order.
|
|
608
|
+
content:
|
|
609
|
+
application/json:
|
|
610
|
+
schema:
|
|
611
|
+
$ref: "#/components/schemas/ApiResponse_BookingCreateResult"
|
|
612
|
+
default:
|
|
613
|
+
$ref: "#/components/responses/ApiError"
|
|
614
|
+
/api/v1/bookings/services:
|
|
615
|
+
get:
|
|
616
|
+
tags: [Bookings]
|
|
617
|
+
operationId: listBookingServices
|
|
618
|
+
summary: List bookable services
|
|
619
|
+
parameters:
|
|
620
|
+
- name: include_inactive
|
|
621
|
+
in: query
|
|
622
|
+
description: >-
|
|
623
|
+
Include services with `active: false`. Defaults to active-only.
|
|
624
|
+
schema:
|
|
625
|
+
type: boolean
|
|
626
|
+
responses:
|
|
627
|
+
"200":
|
|
628
|
+
description: Bookable service catalogue.
|
|
629
|
+
content:
|
|
630
|
+
application/json:
|
|
631
|
+
schema:
|
|
632
|
+
$ref: "#/components/schemas/ApiResponse_BookingServiceArray"
|
|
633
|
+
default:
|
|
634
|
+
$ref: "#/components/responses/ApiError"
|
|
635
|
+
/api/v1/bookings/resources:
|
|
636
|
+
get:
|
|
637
|
+
tags: [Bookings]
|
|
638
|
+
operationId: listBookingResources
|
|
639
|
+
summary: List bookable resources
|
|
640
|
+
responses:
|
|
641
|
+
"200":
|
|
642
|
+
description: Bookable resources — staff, rooms, and equipment.
|
|
643
|
+
content:
|
|
644
|
+
application/json:
|
|
645
|
+
schema:
|
|
646
|
+
$ref: "#/components/schemas/ApiResponse_BookingResourceArray"
|
|
647
|
+
default:
|
|
648
|
+
$ref: "#/components/responses/ApiError"
|
|
649
|
+
/api/v1/bookings/availability:
|
|
650
|
+
get:
|
|
651
|
+
tags: [Bookings]
|
|
652
|
+
operationId: listBookingAvailability
|
|
653
|
+
summary: List free slots for a service
|
|
654
|
+
description: >-
|
|
655
|
+
Slots reflect opening hours, time off, buffers, and existing bookings
|
|
656
|
+
at the moment of the call. They are not held.
|
|
657
|
+
parameters:
|
|
658
|
+
- name: service_id
|
|
659
|
+
in: query
|
|
660
|
+
required: true
|
|
661
|
+
schema:
|
|
662
|
+
type: string
|
|
663
|
+
- name: from_ts
|
|
664
|
+
in: query
|
|
665
|
+
required: true
|
|
666
|
+
description: Start of the window.
|
|
667
|
+
schema:
|
|
668
|
+
$ref: "#/components/schemas/BookingTimestampInput"
|
|
669
|
+
- name: to_ts
|
|
670
|
+
in: query
|
|
671
|
+
required: true
|
|
672
|
+
description: End of the window; must be after `from_ts`.
|
|
673
|
+
schema:
|
|
674
|
+
$ref: "#/components/schemas/BookingTimestampInput"
|
|
675
|
+
- name: resource_id
|
|
676
|
+
in: query
|
|
677
|
+
description: Restrict slots to one resource.
|
|
678
|
+
schema:
|
|
679
|
+
type: string
|
|
680
|
+
responses:
|
|
681
|
+
"200":
|
|
682
|
+
description: Free slots.
|
|
683
|
+
content:
|
|
684
|
+
application/json:
|
|
685
|
+
schema:
|
|
686
|
+
$ref: "#/components/schemas/ApiResponse_BookingSlotArray"
|
|
687
|
+
default:
|
|
688
|
+
$ref: "#/components/responses/ApiError"
|
|
689
|
+
/api/v1/bookings/schedule:
|
|
690
|
+
get:
|
|
691
|
+
tags: [Bookings]
|
|
692
|
+
operationId: listBookingSchedule
|
|
693
|
+
summary: List the dates a service can be booked on
|
|
694
|
+
description: >-
|
|
695
|
+
The other half of `availability`. Availability returns free slots and
|
|
696
|
+
nothing else, so a closed day, an evening past closing and a fully
|
|
697
|
+
booked day all come back as the same empty array. This returns one
|
|
698
|
+
entry per date the workspace keeps opening hours on; a date ABSENT from
|
|
699
|
+
the response is closed. `last_start_ts` is the last start this service
|
|
700
|
+
could occupy on that date (it depends on the service's duration and
|
|
701
|
+
buffers, not just the closing time) and is `null` for a date with
|
|
702
|
+
posted hours that is shut outright, such as a public holiday.
|
|
703
|
+
parameters:
|
|
704
|
+
- name: service_id
|
|
705
|
+
in: query
|
|
706
|
+
required: true
|
|
707
|
+
schema:
|
|
708
|
+
type: string
|
|
709
|
+
- name: from_ts
|
|
710
|
+
in: query
|
|
711
|
+
required: true
|
|
712
|
+
description: Start of the window.
|
|
713
|
+
schema:
|
|
714
|
+
$ref: "#/components/schemas/BookingTimestampInput"
|
|
715
|
+
- name: to_ts
|
|
716
|
+
in: query
|
|
717
|
+
required: true
|
|
718
|
+
description: End of the window; must be after `from_ts`.
|
|
719
|
+
schema:
|
|
720
|
+
$ref: "#/components/schemas/BookingTimestampInput"
|
|
721
|
+
- name: resource_id
|
|
722
|
+
in: query
|
|
723
|
+
description: Restrict to one resource's hours.
|
|
724
|
+
schema:
|
|
725
|
+
type: string
|
|
726
|
+
responses:
|
|
727
|
+
"200":
|
|
728
|
+
description: Open dates.
|
|
729
|
+
content:
|
|
730
|
+
application/json:
|
|
731
|
+
schema:
|
|
732
|
+
$ref: "#/components/schemas/ApiResponse_BookingScheduleDayArray"
|
|
733
|
+
default:
|
|
734
|
+
$ref: "#/components/responses/ApiError"
|
|
735
|
+
/api/v1/bookings/{id}:
|
|
736
|
+
parameters:
|
|
737
|
+
- $ref: "#/components/parameters/Id"
|
|
738
|
+
get:
|
|
739
|
+
tags: [Bookings]
|
|
740
|
+
operationId: getBooking
|
|
741
|
+
summary: Get a booking
|
|
742
|
+
responses:
|
|
743
|
+
"200":
|
|
744
|
+
description: Booking.
|
|
745
|
+
content:
|
|
746
|
+
application/json:
|
|
747
|
+
schema:
|
|
748
|
+
$ref: "#/components/schemas/ApiResponse_Booking"
|
|
749
|
+
default:
|
|
750
|
+
$ref: "#/components/responses/ApiError"
|
|
751
|
+
patch:
|
|
752
|
+
tags: [Bookings]
|
|
753
|
+
operationId: updateBooking
|
|
754
|
+
summary: Annotate a booking
|
|
755
|
+
requestBody:
|
|
756
|
+
required: true
|
|
757
|
+
content:
|
|
758
|
+
application/json:
|
|
759
|
+
schema:
|
|
760
|
+
$ref: "#/components/schemas/UpdateBookingInput"
|
|
761
|
+
responses:
|
|
762
|
+
"200":
|
|
763
|
+
description: The updated booking.
|
|
764
|
+
content:
|
|
765
|
+
application/json:
|
|
766
|
+
schema:
|
|
767
|
+
$ref: "#/components/schemas/ApiResponse_Booking"
|
|
768
|
+
default:
|
|
769
|
+
$ref: "#/components/responses/ApiError"
|
|
770
|
+
/api/v1/bookings/{id}/cancel:
|
|
771
|
+
parameters:
|
|
772
|
+
- $ref: "#/components/parameters/Id"
|
|
773
|
+
post:
|
|
774
|
+
tags: [Bookings]
|
|
775
|
+
operationId: cancelBooking
|
|
776
|
+
summary: Cancel a booking as the business
|
|
777
|
+
description: >-
|
|
778
|
+
Staff semantics — the workspace's cancel window is bypassed and the
|
|
779
|
+
cancellation is attributed to staff.
|
|
780
|
+
requestBody:
|
|
781
|
+
required: true
|
|
782
|
+
content:
|
|
783
|
+
application/json:
|
|
784
|
+
schema:
|
|
785
|
+
$ref: "#/components/schemas/CancelBookingInput"
|
|
786
|
+
responses:
|
|
787
|
+
"200":
|
|
788
|
+
description: Cancel result.
|
|
789
|
+
content:
|
|
790
|
+
application/json:
|
|
791
|
+
schema:
|
|
792
|
+
$ref: "#/components/schemas/ApiResponse_BookingActionResult"
|
|
793
|
+
default:
|
|
794
|
+
$ref: "#/components/responses/ApiError"
|
|
795
|
+
/api/v1/bookings/{id}/reschedule:
|
|
796
|
+
parameters:
|
|
797
|
+
- $ref: "#/components/parameters/Id"
|
|
798
|
+
post:
|
|
799
|
+
tags: [Bookings]
|
|
800
|
+
operationId: rescheduleBooking
|
|
801
|
+
summary: Reschedule a booking as the business
|
|
802
|
+
description: >-
|
|
803
|
+
Staff semantics — the reschedule window is bypassed. The old booking is
|
|
804
|
+
cancelled and a new one inserted, so the response carries a new booking
|
|
805
|
+
id and a freshly minted manage token.
|
|
806
|
+
requestBody:
|
|
807
|
+
required: true
|
|
808
|
+
content:
|
|
809
|
+
application/json:
|
|
810
|
+
schema:
|
|
811
|
+
$ref: "#/components/schemas/RescheduleBookingInput"
|
|
812
|
+
responses:
|
|
813
|
+
"200":
|
|
814
|
+
description: Reschedule result.
|
|
815
|
+
content:
|
|
816
|
+
application/json:
|
|
817
|
+
schema:
|
|
818
|
+
$ref: "#/components/schemas/ApiResponse_BookingRescheduleResult"
|
|
819
|
+
default:
|
|
820
|
+
$ref: "#/components/responses/ApiError"
|
|
821
|
+
/api/v1/bookings/{id}/no-show:
|
|
822
|
+
parameters:
|
|
823
|
+
- $ref: "#/components/parameters/Id"
|
|
824
|
+
post:
|
|
825
|
+
tags: [Bookings]
|
|
826
|
+
operationId: markBookingNoShow
|
|
827
|
+
summary: Mark a booking as a no-show
|
|
828
|
+
responses:
|
|
829
|
+
"200":
|
|
830
|
+
description: No-show result.
|
|
831
|
+
content:
|
|
832
|
+
application/json:
|
|
833
|
+
schema:
|
|
834
|
+
$ref: "#/components/schemas/ApiResponse_BookingActionResult"
|
|
835
|
+
default:
|
|
836
|
+
$ref: "#/components/responses/ApiError"
|
|
837
|
+
/api/v1/bookings/manage/{token}:
|
|
838
|
+
parameters:
|
|
839
|
+
- $ref: "#/components/parameters/ManageToken"
|
|
840
|
+
get:
|
|
841
|
+
tags: [Bookings]
|
|
842
|
+
operationId: getManagedBooking
|
|
843
|
+
summary: Get the customer manage summary for a token
|
|
844
|
+
responses:
|
|
845
|
+
"200":
|
|
846
|
+
description: >-
|
|
847
|
+
Manage summary. An unknown token and a token belonging to another
|
|
848
|
+
workspace both answer 404, so this is not an existence oracle.
|
|
849
|
+
content:
|
|
850
|
+
application/json:
|
|
851
|
+
schema:
|
|
852
|
+
$ref: "#/components/schemas/ApiResponse_ManageSummary"
|
|
853
|
+
default:
|
|
854
|
+
$ref: "#/components/responses/ApiError"
|
|
855
|
+
/api/v1/bookings/manage/{token}/cancel:
|
|
856
|
+
parameters:
|
|
857
|
+
- $ref: "#/components/parameters/ManageToken"
|
|
858
|
+
post:
|
|
859
|
+
tags: [Bookings]
|
|
860
|
+
operationId: cancelManagedBooking
|
|
861
|
+
summary: Cancel on the customer's behalf
|
|
862
|
+
description: >-
|
|
863
|
+
Customer semantics — the workspace's cancel window is ENFORCED and the
|
|
864
|
+
cancellation is attributed to the customer.
|
|
865
|
+
requestBody:
|
|
866
|
+
required: true
|
|
867
|
+
content:
|
|
868
|
+
application/json:
|
|
869
|
+
schema:
|
|
870
|
+
$ref: "#/components/schemas/CancelBookingInput"
|
|
871
|
+
responses:
|
|
872
|
+
"200":
|
|
873
|
+
description: Cancel result.
|
|
874
|
+
content:
|
|
875
|
+
application/json:
|
|
876
|
+
schema:
|
|
877
|
+
$ref: "#/components/schemas/ApiResponse_BookingActionResult"
|
|
878
|
+
default:
|
|
879
|
+
$ref: "#/components/responses/ApiError"
|
|
880
|
+
/api/v1/bookings/manage/{token}/reschedule:
|
|
881
|
+
parameters:
|
|
882
|
+
- $ref: "#/components/parameters/ManageToken"
|
|
883
|
+
post:
|
|
884
|
+
tags: [Bookings]
|
|
885
|
+
operationId: rescheduleManagedBooking
|
|
886
|
+
summary: Reschedule on the customer's behalf
|
|
887
|
+
description: >-
|
|
888
|
+
Customer semantics — the reschedule window is ENFORCED. The response
|
|
889
|
+
carries a new booking id and a new manage token; the old token stops
|
|
890
|
+
working.
|
|
891
|
+
requestBody:
|
|
892
|
+
required: true
|
|
893
|
+
content:
|
|
894
|
+
application/json:
|
|
895
|
+
schema:
|
|
896
|
+
$ref: "#/components/schemas/RescheduleBookingInput"
|
|
897
|
+
responses:
|
|
898
|
+
"200":
|
|
899
|
+
description: Reschedule result.
|
|
900
|
+
content:
|
|
901
|
+
application/json:
|
|
902
|
+
schema:
|
|
903
|
+
$ref: "#/components/schemas/ApiResponse_BookingRescheduleResult"
|
|
904
|
+
default:
|
|
905
|
+
$ref: "#/components/responses/ApiError"
|
|
906
|
+
/api/v1/portal/login/start:
|
|
907
|
+
post:
|
|
908
|
+
tags: [Portal]
|
|
909
|
+
operationId: startPortalLogin
|
|
910
|
+
summary: E-mail a one-time login code
|
|
911
|
+
description: >-
|
|
912
|
+
Always answers 202 `{ status: "sent" }`, whether or not the address
|
|
913
|
+
belongs to a contact — enumeration-safe by design. Rate-limited per
|
|
914
|
+
address and per caller (`429 RATE_LIMITED`). Not idempotency-keyed.
|
|
915
|
+
Errors: `400 VALIDATION_ERROR`, `403 FORBIDDEN` (key lacks
|
|
916
|
+
`write:portal`), `429 RATE_LIMITED`.
|
|
917
|
+
requestBody:
|
|
918
|
+
required: true
|
|
919
|
+
content:
|
|
920
|
+
application/json:
|
|
921
|
+
schema:
|
|
922
|
+
$ref: "#/components/schemas/PortalLoginStartInput"
|
|
923
|
+
responses:
|
|
924
|
+
"202":
|
|
925
|
+
description: Code queued (or silently not, for an unknown address).
|
|
926
|
+
content:
|
|
927
|
+
application/json:
|
|
928
|
+
schema:
|
|
929
|
+
$ref: "#/components/schemas/ApiResponse_PortalLoginStartResult"
|
|
930
|
+
default:
|
|
931
|
+
$ref: "#/components/responses/ApiError"
|
|
932
|
+
/api/v1/portal/login/verify:
|
|
933
|
+
post:
|
|
934
|
+
tags: [Portal]
|
|
935
|
+
operationId: verifyPortalLogin
|
|
936
|
+
summary: Exchange the e-mailed code for a session
|
|
937
|
+
description: >-
|
|
938
|
+
A wrong, burned or expired code all answer `401 PORTAL_CODE_INVALID` —
|
|
939
|
+
the three are not distinguished, so the response is not an oracle for
|
|
940
|
+
which codes exist. The returned `session_token` is a bearer credential
|
|
941
|
+
for ONE contact: keep it in an HttpOnly cookie on the site's server and
|
|
942
|
+
never hand it to the browser. Not idempotency-keyed. Errors:
|
|
943
|
+
`400 VALIDATION_ERROR`, `401 PORTAL_CODE_INVALID`, `403 FORBIDDEN`,
|
|
944
|
+
`429 RATE_LIMITED`.
|
|
945
|
+
requestBody:
|
|
946
|
+
required: true
|
|
947
|
+
content:
|
|
948
|
+
application/json:
|
|
949
|
+
schema:
|
|
950
|
+
$ref: "#/components/schemas/PortalVerifyInput"
|
|
951
|
+
responses:
|
|
952
|
+
"200":
|
|
953
|
+
description: The new session.
|
|
954
|
+
content:
|
|
955
|
+
application/json:
|
|
956
|
+
schema:
|
|
957
|
+
$ref: "#/components/schemas/ApiResponse_PortalSession"
|
|
958
|
+
default:
|
|
959
|
+
$ref: "#/components/responses/ApiError"
|
|
960
|
+
/api/v1/portal/logout:
|
|
961
|
+
parameters:
|
|
962
|
+
- $ref: "#/components/parameters/PortalSession"
|
|
963
|
+
post:
|
|
964
|
+
tags: [Portal]
|
|
965
|
+
operationId: logoutPortal
|
|
966
|
+
summary: Revoke the session
|
|
967
|
+
description: >-
|
|
968
|
+
Revoking twice reaches the same state — the second call answers
|
|
969
|
+
`401 PORTAL_SESSION_INVALID`. Not idempotency-keyed. Errors:
|
|
970
|
+
`401 PORTAL_SESSION_REQUIRED` (header missing),
|
|
971
|
+
`401 PORTAL_SESSION_INVALID` (unknown, expired or revoked),
|
|
972
|
+
`403 FORBIDDEN`, `429 RATE_LIMITED`.
|
|
973
|
+
responses:
|
|
974
|
+
"204":
|
|
975
|
+
description: Session revoked.
|
|
976
|
+
default:
|
|
977
|
+
$ref: "#/components/responses/ApiError"
|
|
978
|
+
/api/v1/portal/me:
|
|
979
|
+
parameters:
|
|
980
|
+
- $ref: "#/components/parameters/PortalSession"
|
|
981
|
+
get:
|
|
982
|
+
tags: [Portal]
|
|
983
|
+
operationId: getPortalProfile
|
|
984
|
+
summary: The signed-in contact's profile
|
|
985
|
+
description: >-
|
|
986
|
+
Errors: `401 PORTAL_SESSION_REQUIRED`, `401 PORTAL_SESSION_INVALID`,
|
|
987
|
+
`403 FORBIDDEN` (key lacks `read:portal`), `429 RATE_LIMITED`.
|
|
988
|
+
responses:
|
|
989
|
+
"200":
|
|
990
|
+
description: Profile.
|
|
991
|
+
content:
|
|
992
|
+
application/json:
|
|
993
|
+
schema:
|
|
994
|
+
$ref: "#/components/schemas/ApiResponse_PortalProfile"
|
|
995
|
+
default:
|
|
996
|
+
$ref: "#/components/responses/ApiError"
|
|
997
|
+
patch:
|
|
998
|
+
tags: [Portal]
|
|
999
|
+
operationId: updatePortalProfile
|
|
1000
|
+
summary: Update the signed-in contact's profile
|
|
1001
|
+
description: >-
|
|
1002
|
+
Only the supplied fields change. `phone: null` clears the number,
|
|
1003
|
+
`family` replaces the whole list, and `marketing_consent` records a
|
|
1004
|
+
`marketing_email` consent decision with source `portal`. Answers the
|
|
1005
|
+
profile as it is after the change. Errors: `400 VALIDATION_ERROR`,
|
|
1006
|
+
`401 PORTAL_SESSION_REQUIRED`, `401 PORTAL_SESSION_INVALID`,
|
|
1007
|
+
`403 FORBIDDEN` (key lacks `write:portal`), `429 RATE_LIMITED`.
|
|
1008
|
+
requestBody:
|
|
1009
|
+
required: true
|
|
1010
|
+
content:
|
|
1011
|
+
application/json:
|
|
1012
|
+
schema:
|
|
1013
|
+
$ref: "#/components/schemas/PortalProfilePatch"
|
|
1014
|
+
responses:
|
|
1015
|
+
"200":
|
|
1016
|
+
description: Updated profile.
|
|
1017
|
+
content:
|
|
1018
|
+
application/json:
|
|
1019
|
+
schema:
|
|
1020
|
+
$ref: "#/components/schemas/ApiResponse_PortalProfile"
|
|
1021
|
+
default:
|
|
1022
|
+
$ref: "#/components/responses/ApiError"
|
|
1023
|
+
/api/v1/portal/me/bookings:
|
|
1024
|
+
parameters:
|
|
1025
|
+
- $ref: "#/components/parameters/PortalSession"
|
|
1026
|
+
get:
|
|
1027
|
+
tags: [Portal]
|
|
1028
|
+
operationId: listPortalBookings
|
|
1029
|
+
summary: The signed-in contact's bookings
|
|
1030
|
+
description: >-
|
|
1031
|
+
Split into `upcoming` and `past`. An upcoming booking still inside the
|
|
1032
|
+
workspace's policy windows carries `manage_token` and
|
|
1033
|
+
`can_manage: true`; the token opens the site's manage page. Errors:
|
|
1034
|
+
`401 PORTAL_SESSION_REQUIRED`, `401 PORTAL_SESSION_INVALID`,
|
|
1035
|
+
`403 FORBIDDEN`, `429 RATE_LIMITED`.
|
|
1036
|
+
responses:
|
|
1037
|
+
"200":
|
|
1038
|
+
description: Bookings.
|
|
1039
|
+
content:
|
|
1040
|
+
application/json:
|
|
1041
|
+
schema:
|
|
1042
|
+
$ref: "#/components/schemas/ApiResponse_PortalBookings"
|
|
1043
|
+
default:
|
|
1044
|
+
$ref: "#/components/responses/ApiError"
|
|
1045
|
+
/api/v1/portal/me/export:
|
|
1046
|
+
parameters:
|
|
1047
|
+
- $ref: "#/components/parameters/PortalSession"
|
|
1048
|
+
post:
|
|
1049
|
+
tags: [Portal]
|
|
1050
|
+
operationId: exportPortalData
|
|
1051
|
+
summary: Export everything held about the signed-in contact
|
|
1052
|
+
description: >-
|
|
1053
|
+
A synchronous GDPR Art. 15 export of the contact's profile, family,
|
|
1054
|
+
consents and bookings, as one JSON document. Read-only, so not
|
|
1055
|
+
idempotency-keyed. Errors: `401 PORTAL_SESSION_REQUIRED`,
|
|
1056
|
+
`401 PORTAL_SESSION_INVALID`, `403 FORBIDDEN`, `429 RATE_LIMITED`.
|
|
1057
|
+
responses:
|
|
1058
|
+
"200":
|
|
1059
|
+
description: The export.
|
|
1060
|
+
content:
|
|
1061
|
+
application/json:
|
|
1062
|
+
schema:
|
|
1063
|
+
$ref: "#/components/schemas/ApiResponse_PortalExport"
|
|
1064
|
+
default:
|
|
1065
|
+
$ref: "#/components/responses/ApiError"
|
|
1066
|
+
/api/v1/portal/me/delete:
|
|
1067
|
+
parameters:
|
|
1068
|
+
- $ref: "#/components/parameters/PortalSession"
|
|
1069
|
+
post:
|
|
1070
|
+
tags: [Portal]
|
|
1071
|
+
operationId: deletePortalAccount
|
|
1072
|
+
summary: Erase the signed-in contact
|
|
1073
|
+
description: >-
|
|
1074
|
+
GDPR Art. 17 erasure. The session is revoked as part of the deletion,
|
|
1075
|
+
so a retry answers `401 PORTAL_SESSION_INVALID` rather than deleting
|
|
1076
|
+
anything else. Not idempotency-keyed. Errors:
|
|
1077
|
+
`401 PORTAL_SESSION_REQUIRED`, `401 PORTAL_SESSION_INVALID`,
|
|
1078
|
+
`403 FORBIDDEN` (key lacks `write:portal`), `429 RATE_LIMITED`.
|
|
1079
|
+
responses:
|
|
1080
|
+
"204":
|
|
1081
|
+
description: Contact erased and session revoked.
|
|
1082
|
+
default:
|
|
1083
|
+
$ref: "#/components/responses/ApiError"
|
|
546
1084
|
/api/v1/gdpr/export:
|
|
547
1085
|
post:
|
|
548
1086
|
tags: [GDPR]
|
|
@@ -1177,6 +1715,27 @@ components:
|
|
|
1177
1715
|
required: true
|
|
1178
1716
|
schema:
|
|
1179
1717
|
type: string
|
|
1718
|
+
ManageToken:
|
|
1719
|
+
name: token
|
|
1720
|
+
in: path
|
|
1721
|
+
required: true
|
|
1722
|
+
description: >-
|
|
1723
|
+
The show-once manage token handed out when the booking was created.
|
|
1724
|
+
Possession of it authorizes the customer's own cancel or reschedule.
|
|
1725
|
+
schema:
|
|
1726
|
+
type: string
|
|
1727
|
+
PortalSession:
|
|
1728
|
+
name: X-Portal-Session
|
|
1729
|
+
in: header
|
|
1730
|
+
required: true
|
|
1731
|
+
description: >-
|
|
1732
|
+
The `session_token` returned by `verifyPortalLogin`. A bearer
|
|
1733
|
+
credential for ONE contact — the site's server keeps it in an HttpOnly
|
|
1734
|
+
cookie and forwards it here. Missing answers
|
|
1735
|
+
`401 PORTAL_SESSION_REQUIRED`; unknown, expired or revoked answers
|
|
1736
|
+
`401 PORTAL_SESSION_INVALID`.
|
|
1737
|
+
schema:
|
|
1738
|
+
type: string
|
|
1180
1739
|
Cursor:
|
|
1181
1740
|
name: cursor
|
|
1182
1741
|
in: query
|
|
@@ -2035,22 +2594,512 @@ components:
|
|
|
2035
2594
|
type: string
|
|
2036
2595
|
brand_website:
|
|
2037
2596
|
type: string
|
|
2038
|
-
format: uri
|
|
2597
|
+
format: uri
|
|
2598
|
+
contact_id:
|
|
2599
|
+
type: [string, "null"]
|
|
2600
|
+
contact_name:
|
|
2601
|
+
type: string
|
|
2602
|
+
contact_email:
|
|
2603
|
+
type: string
|
|
2604
|
+
format: email
|
|
2605
|
+
start_date:
|
|
2606
|
+
type: string
|
|
2607
|
+
format: date
|
|
2608
|
+
end_date:
|
|
2609
|
+
type: string
|
|
2610
|
+
format: date
|
|
2611
|
+
notes:
|
|
2612
|
+
type: string
|
|
2613
|
+
BookingTimestampInput:
|
|
2614
|
+
description: >-
|
|
2615
|
+
A timestamp on the way in — Unix milliseconds, or an ISO 8601
|
|
2616
|
+
date-time string. Responses always render timestamps as ISO 8601.
|
|
2617
|
+
type: [integer, string]
|
|
2618
|
+
BookingStatus:
|
|
2619
|
+
type: string
|
|
2620
|
+
enum: [pending, confirmed, completed, cancelled, no_show]
|
|
2621
|
+
BookingCancelledBy:
|
|
2622
|
+
description: >-
|
|
2623
|
+
Who cancelled. `staff` for the id-addressed cancel, `customer` for the
|
|
2624
|
+
manage-token cancel, `system` for an automated one.
|
|
2625
|
+
type: string
|
|
2626
|
+
enum: [customer, staff, system]
|
|
2627
|
+
BookingPaymentStatus:
|
|
2628
|
+
type: string
|
|
2629
|
+
enum: [none, reserved, captured, refunded]
|
|
2630
|
+
BookingCreatedVia:
|
|
2631
|
+
type: string
|
|
2632
|
+
enum: [web, dashboard, walk_in, api]
|
|
2633
|
+
BookingResourceType:
|
|
2634
|
+
type: string
|
|
2635
|
+
enum: [staff, room, equipment]
|
|
2636
|
+
Booking:
|
|
2637
|
+
type: object
|
|
2638
|
+
required:
|
|
2639
|
+
- id
|
|
2640
|
+
- contact_id
|
|
2641
|
+
- service_id
|
|
2642
|
+
- resource_id
|
|
2643
|
+
- start_ts
|
|
2644
|
+
- end_ts
|
|
2645
|
+
- booked_for_name
|
|
2646
|
+
- booked_for_birth_year
|
|
2647
|
+
- party_sequence_id
|
|
2648
|
+
- status
|
|
2649
|
+
- cancelled_by
|
|
2650
|
+
- cancel_reason
|
|
2651
|
+
- rescheduled_from_id
|
|
2652
|
+
- payment_status
|
|
2653
|
+
- amount_ore
|
|
2654
|
+
- notes
|
|
2655
|
+
- internal_notes
|
|
2656
|
+
- created_via
|
|
2657
|
+
- created_at
|
|
2658
|
+
- updated_at
|
|
2659
|
+
properties:
|
|
2660
|
+
id:
|
|
2661
|
+
type: string
|
|
2662
|
+
contact_id:
|
|
2663
|
+
type: [string, "null"]
|
|
2664
|
+
service_id:
|
|
2665
|
+
type: [string, "null"]
|
|
2666
|
+
resource_id:
|
|
2667
|
+
type: [string, "null"]
|
|
2668
|
+
start_ts:
|
|
2669
|
+
type: [string, "null"]
|
|
2670
|
+
format: date-time
|
|
2671
|
+
end_ts:
|
|
2672
|
+
type: [string, "null"]
|
|
2673
|
+
format: date-time
|
|
2674
|
+
booked_for_name:
|
|
2675
|
+
type: [string, "null"]
|
|
2676
|
+
booked_for_birth_year:
|
|
2677
|
+
description: A birth year, not a birthdate — the age bracket is all that is stored.
|
|
2678
|
+
type: [integer, "null"]
|
|
2679
|
+
party_sequence_id:
|
|
2680
|
+
description: Shared by every booking created in the same party request.
|
|
2681
|
+
type: [string, "null"]
|
|
2682
|
+
status:
|
|
2683
|
+
$ref: "#/components/schemas/BookingStatus"
|
|
2684
|
+
cancelled_by:
|
|
2685
|
+
oneOf:
|
|
2686
|
+
- $ref: "#/components/schemas/BookingCancelledBy"
|
|
2687
|
+
- type: "null"
|
|
2688
|
+
cancel_reason:
|
|
2689
|
+
type: [string, "null"]
|
|
2690
|
+
rescheduled_from_id:
|
|
2691
|
+
description: On a booking created by a reschedule, the booking it replaced.
|
|
2692
|
+
type: [string, "null"]
|
|
2693
|
+
payment_status:
|
|
2694
|
+
$ref: "#/components/schemas/BookingPaymentStatus"
|
|
2695
|
+
amount_ore:
|
|
2696
|
+
description: Price in integer øre. Never a float and never kroner.
|
|
2697
|
+
type: [integer, "null"]
|
|
2698
|
+
notes:
|
|
2699
|
+
description: Customer-visible note.
|
|
2700
|
+
type: [string, "null"]
|
|
2701
|
+
internal_notes:
|
|
2702
|
+
description: Staff-only note; never shown to the customer.
|
|
2703
|
+
type: [string, "null"]
|
|
2704
|
+
created_via:
|
|
2705
|
+
oneOf:
|
|
2706
|
+
- $ref: "#/components/schemas/BookingCreatedVia"
|
|
2707
|
+
- type: "null"
|
|
2708
|
+
created_at:
|
|
2709
|
+
type: [string, "null"]
|
|
2710
|
+
format: date-time
|
|
2711
|
+
updated_at:
|
|
2712
|
+
type: [string, "null"]
|
|
2713
|
+
format: date-time
|
|
2714
|
+
BookingService:
|
|
2715
|
+
type: object
|
|
2716
|
+
required:
|
|
2717
|
+
- id
|
|
2718
|
+
- name
|
|
2719
|
+
- description
|
|
2720
|
+
- category
|
|
2721
|
+
- duration_minutes
|
|
2722
|
+
- buffer_before_minutes
|
|
2723
|
+
- buffer_after_minutes
|
|
2724
|
+
- price_ore
|
|
2725
|
+
- weekend_surcharge_pct
|
|
2726
|
+
- resource_requirements
|
|
2727
|
+
- bookable_online
|
|
2728
|
+
- max_per_booking
|
|
2729
|
+
- color
|
|
2730
|
+
- sort_order
|
|
2731
|
+
- active
|
|
2732
|
+
- created_at
|
|
2733
|
+
- updated_at
|
|
2734
|
+
properties:
|
|
2735
|
+
id:
|
|
2736
|
+
type: string
|
|
2737
|
+
name:
|
|
2738
|
+
type: [string, "null"]
|
|
2739
|
+
description:
|
|
2740
|
+
type: [string, "null"]
|
|
2741
|
+
category:
|
|
2742
|
+
type: [string, "null"]
|
|
2743
|
+
duration_minutes:
|
|
2744
|
+
type: [integer, "null"]
|
|
2745
|
+
buffer_before_minutes:
|
|
2746
|
+
type: [integer, "null"]
|
|
2747
|
+
buffer_after_minutes:
|
|
2748
|
+
type: [integer, "null"]
|
|
2749
|
+
price_ore:
|
|
2750
|
+
description: Price in integer øre.
|
|
2751
|
+
type: [integer, "null"]
|
|
2752
|
+
weekend_surcharge_pct:
|
|
2753
|
+
type: [number, "null"]
|
|
2754
|
+
resource_requirements:
|
|
2755
|
+
description: Resource types this service needs, e.g. `["staff"]`.
|
|
2756
|
+
type: array
|
|
2757
|
+
items:
|
|
2758
|
+
type: string
|
|
2759
|
+
bookable_online:
|
|
2760
|
+
type: boolean
|
|
2761
|
+
max_per_booking:
|
|
2762
|
+
type: [integer, "null"]
|
|
2763
|
+
color:
|
|
2764
|
+
type: [string, "null"]
|
|
2765
|
+
sort_order:
|
|
2766
|
+
type: [integer, "null"]
|
|
2767
|
+
active:
|
|
2768
|
+
type: boolean
|
|
2769
|
+
created_at:
|
|
2770
|
+
type: [string, "null"]
|
|
2771
|
+
format: date-time
|
|
2772
|
+
updated_at:
|
|
2773
|
+
type: [string, "null"]
|
|
2774
|
+
format: date-time
|
|
2775
|
+
BookingResource:
|
|
2776
|
+
type: object
|
|
2777
|
+
required:
|
|
2778
|
+
- id
|
|
2779
|
+
- type
|
|
2780
|
+
- name
|
|
2781
|
+
- photo_url
|
|
2782
|
+
- bio
|
|
2783
|
+
- service_ids
|
|
2784
|
+
- capacity
|
|
2785
|
+
- sort_order
|
|
2786
|
+
- active
|
|
2787
|
+
- created_at
|
|
2788
|
+
- updated_at
|
|
2789
|
+
properties:
|
|
2790
|
+
id:
|
|
2791
|
+
type: string
|
|
2792
|
+
type:
|
|
2793
|
+
oneOf:
|
|
2794
|
+
- $ref: "#/components/schemas/BookingResourceType"
|
|
2795
|
+
- type: "null"
|
|
2796
|
+
name:
|
|
2797
|
+
type: [string, "null"]
|
|
2798
|
+
photo_url:
|
|
2799
|
+
type: [string, "null"]
|
|
2800
|
+
format: uri
|
|
2801
|
+
bio:
|
|
2802
|
+
type: [string, "null"]
|
|
2803
|
+
service_ids:
|
|
2804
|
+
description: Services this resource can perform.
|
|
2805
|
+
type: array
|
|
2806
|
+
items:
|
|
2807
|
+
type: string
|
|
2808
|
+
capacity:
|
|
2809
|
+
type: [integer, "null"]
|
|
2810
|
+
sort_order:
|
|
2811
|
+
type: [integer, "null"]
|
|
2812
|
+
active:
|
|
2813
|
+
type: boolean
|
|
2814
|
+
created_at:
|
|
2815
|
+
type: [string, "null"]
|
|
2816
|
+
format: date-time
|
|
2817
|
+
updated_at:
|
|
2818
|
+
type: [string, "null"]
|
|
2819
|
+
format: date-time
|
|
2820
|
+
BookingSlot:
|
|
2821
|
+
type: object
|
|
2822
|
+
required: [start_ts, end_ts, resource_id]
|
|
2823
|
+
properties:
|
|
2824
|
+
start_ts:
|
|
2825
|
+
type: [string, "null"]
|
|
2826
|
+
format: date-time
|
|
2827
|
+
end_ts:
|
|
2828
|
+
type: [string, "null"]
|
|
2829
|
+
format: date-time
|
|
2830
|
+
resource_id:
|
|
2831
|
+
type: [string, "null"]
|
|
2832
|
+
BookingScheduleDay:
|
|
2833
|
+
description: One date a service can be booked on. `date` is the workspace's own calendar date, not a timestamp.
|
|
2834
|
+
type: object
|
|
2835
|
+
required: [date, opens_ts, closes_ts, last_start_ts]
|
|
2836
|
+
properties:
|
|
2837
|
+
date:
|
|
2838
|
+
type: [string, "null"]
|
|
2839
|
+
format: date
|
|
2840
|
+
opens_ts:
|
|
2841
|
+
type: [string, "null"]
|
|
2842
|
+
format: date-time
|
|
2843
|
+
closes_ts:
|
|
2844
|
+
type: [string, "null"]
|
|
2845
|
+
format: date-time
|
|
2846
|
+
last_start_ts:
|
|
2847
|
+
description: Last start this service could occupy on the date; `null` when the date is shut outright.
|
|
2848
|
+
type: [string, "null"]
|
|
2849
|
+
format: date-time
|
|
2850
|
+
BookingContactInput:
|
|
2851
|
+
description: The person the booking is made under. Phone is the CRM dedupe key.
|
|
2852
|
+
type: object
|
|
2853
|
+
required: [phone]
|
|
2854
|
+
properties:
|
|
2855
|
+
phone:
|
|
2856
|
+
type: string
|
|
2857
|
+
maxLength: 40
|
|
2858
|
+
email:
|
|
2859
|
+
type: string
|
|
2860
|
+
maxLength: 320
|
|
2861
|
+
name:
|
|
2862
|
+
type: string
|
|
2863
|
+
maxLength: 200
|
|
2864
|
+
CreateBookingItemInput:
|
|
2865
|
+
description: One line of a party — a single service on a single slot.
|
|
2866
|
+
type: object
|
|
2867
|
+
required: [service_id, start_ts]
|
|
2868
|
+
properties:
|
|
2869
|
+
service_id:
|
|
2870
|
+
type: string
|
|
2871
|
+
resource_id:
|
|
2872
|
+
description: Omit to let the engine pick a free resource.
|
|
2873
|
+
type: string
|
|
2874
|
+
start_ts:
|
|
2875
|
+
$ref: "#/components/schemas/BookingTimestampInput"
|
|
2876
|
+
booked_for_name:
|
|
2877
|
+
type: string
|
|
2878
|
+
maxLength: 200
|
|
2879
|
+
booked_for_birth_year:
|
|
2880
|
+
type: integer
|
|
2881
|
+
minimum: 1900
|
|
2882
|
+
maximum: 2200
|
|
2883
|
+
CreateBookingInput:
|
|
2884
|
+
type: object
|
|
2885
|
+
required: [items, contact]
|
|
2886
|
+
properties:
|
|
2887
|
+
items:
|
|
2888
|
+
description: >-
|
|
2889
|
+
A PARTY — one request books a whole family in one all-or-nothing
|
|
2890
|
+
transaction.
|
|
2891
|
+
type: array
|
|
2892
|
+
minItems: 1
|
|
2893
|
+
maxItems: 50
|
|
2894
|
+
items:
|
|
2895
|
+
$ref: "#/components/schemas/CreateBookingItemInput"
|
|
2896
|
+
contact:
|
|
2897
|
+
$ref: "#/components/schemas/BookingContactInput"
|
|
2898
|
+
notes:
|
|
2899
|
+
type: string
|
|
2900
|
+
maxLength: 5000
|
|
2901
|
+
created_via:
|
|
2902
|
+
description: >-
|
|
2903
|
+
Where the booking came from. Defaults to `api`. A workspace's OWN
|
|
2904
|
+
website should send `web`, so its bookings can be told apart from
|
|
2905
|
+
integrations. `dashboard` and `walk_in` are staff-only and are
|
|
2906
|
+
rejected with 400 — a bearer token proves which workspace is calling,
|
|
2907
|
+
not that a member typed the booking in.
|
|
2908
|
+
type: string
|
|
2909
|
+
enum: [web, api]
|
|
2910
|
+
CreatedBooking:
|
|
2911
|
+
type: object
|
|
2912
|
+
required: [id]
|
|
2913
|
+
properties:
|
|
2914
|
+
id:
|
|
2915
|
+
type: string
|
|
2916
|
+
manage_token:
|
|
2917
|
+
description: >-
|
|
2918
|
+
Show-once secret for the customer's manage link. Only its hash is
|
|
2919
|
+
stored, and an idempotent replay omits this field entirely.
|
|
2920
|
+
type: string
|
|
2921
|
+
BookingCreateResult:
|
|
2922
|
+
type: object
|
|
2923
|
+
required: [bookings, contact_id]
|
|
2924
|
+
properties:
|
|
2925
|
+
bookings:
|
|
2926
|
+
description: One entry per created booking, in request order.
|
|
2927
|
+
type: array
|
|
2928
|
+
items:
|
|
2929
|
+
$ref: "#/components/schemas/CreatedBooking"
|
|
2930
|
+
contact_id:
|
|
2931
|
+
type: string
|
|
2932
|
+
BookingActionResult:
|
|
2933
|
+
type: object
|
|
2934
|
+
required: [success]
|
|
2935
|
+
properties:
|
|
2936
|
+
success:
|
|
2937
|
+
type: boolean
|
|
2938
|
+
const: true
|
|
2939
|
+
BookingRescheduleResult:
|
|
2940
|
+
type: object
|
|
2941
|
+
required: [success, booking_id]
|
|
2942
|
+
properties:
|
|
2943
|
+
success:
|
|
2944
|
+
type: boolean
|
|
2945
|
+
const: true
|
|
2946
|
+
booking_id:
|
|
2947
|
+
description: >-
|
|
2948
|
+
The NEW booking's id — a reschedule cancels the old row and inserts
|
|
2949
|
+
a new one.
|
|
2950
|
+
type: string
|
|
2951
|
+
manage_token:
|
|
2952
|
+
description: >-
|
|
2953
|
+
Freshly minted manage token for the new booking; omitted on an
|
|
2954
|
+
idempotent replay.
|
|
2955
|
+
type: string
|
|
2956
|
+
ManageSummary:
|
|
2957
|
+
description: >-
|
|
2958
|
+
What the holder of a manage token may see and do. `can_cancel` and
|
|
2959
|
+
`can_reschedule` already apply the workspace's policy windows.
|
|
2960
|
+
type: object
|
|
2961
|
+
required:
|
|
2962
|
+
- booking_id
|
|
2963
|
+
- contact_id
|
|
2964
|
+
- status
|
|
2965
|
+
- cancelled_by
|
|
2966
|
+
- cancel_reason
|
|
2967
|
+
- rescheduled_from_id
|
|
2968
|
+
- start_ts
|
|
2969
|
+
- end_ts
|
|
2970
|
+
- service_id
|
|
2971
|
+
- service_name
|
|
2972
|
+
- resource_id
|
|
2973
|
+
- resource_name
|
|
2974
|
+
- booked_for_name
|
|
2975
|
+
- party_sequence_id
|
|
2976
|
+
- amount_ore
|
|
2977
|
+
- payment_status
|
|
2978
|
+
- time_zone
|
|
2979
|
+
- cancel_window_hours
|
|
2980
|
+
- reschedule_window_hours
|
|
2981
|
+
- can_cancel
|
|
2982
|
+
- can_reschedule
|
|
2983
|
+
properties:
|
|
2984
|
+
booking_id:
|
|
2985
|
+
type: string
|
|
2039
2986
|
contact_id:
|
|
2040
2987
|
type: [string, "null"]
|
|
2041
|
-
|
|
2042
|
-
|
|
2043
|
-
|
|
2044
|
-
|
|
2045
|
-
|
|
2046
|
-
|
|
2047
|
-
|
|
2048
|
-
|
|
2049
|
-
|
|
2988
|
+
status:
|
|
2989
|
+
oneOf:
|
|
2990
|
+
- $ref: "#/components/schemas/BookingStatus"
|
|
2991
|
+
- type: "null"
|
|
2992
|
+
cancelled_by:
|
|
2993
|
+
oneOf:
|
|
2994
|
+
- $ref: "#/components/schemas/BookingCancelledBy"
|
|
2995
|
+
- type: "null"
|
|
2996
|
+
cancel_reason:
|
|
2997
|
+
type: [string, "null"]
|
|
2998
|
+
rescheduled_from_id:
|
|
2999
|
+
type: [string, "null"]
|
|
3000
|
+
start_ts:
|
|
3001
|
+
type: [string, "null"]
|
|
3002
|
+
format: date-time
|
|
3003
|
+
end_ts:
|
|
3004
|
+
type: [string, "null"]
|
|
3005
|
+
format: date-time
|
|
3006
|
+
service_id:
|
|
3007
|
+
type: [string, "null"]
|
|
3008
|
+
service_name:
|
|
3009
|
+
type: [string, "null"]
|
|
3010
|
+
resource_id:
|
|
3011
|
+
type: [string, "null"]
|
|
3012
|
+
resource_name:
|
|
3013
|
+
type: [string, "null"]
|
|
3014
|
+
booked_for_name:
|
|
3015
|
+
type: [string, "null"]
|
|
3016
|
+
party_sequence_id:
|
|
3017
|
+
type: [string, "null"]
|
|
3018
|
+
amount_ore:
|
|
3019
|
+
description: Price in integer øre.
|
|
3020
|
+
type: [integer, "null"]
|
|
3021
|
+
payment_status:
|
|
3022
|
+
oneOf:
|
|
3023
|
+
- $ref: "#/components/schemas/BookingPaymentStatus"
|
|
3024
|
+
- type: "null"
|
|
3025
|
+
time_zone:
|
|
3026
|
+
description: IANA zone the booking's local times should be rendered in.
|
|
3027
|
+
type: [string, "null"]
|
|
3028
|
+
cancel_window_hours:
|
|
3029
|
+
type: [number, "null"]
|
|
3030
|
+
reschedule_window_hours:
|
|
3031
|
+
type: [number, "null"]
|
|
3032
|
+
can_cancel:
|
|
3033
|
+
type: boolean
|
|
3034
|
+
can_reschedule:
|
|
3035
|
+
type: boolean
|
|
3036
|
+
UpdateBookingInput:
|
|
3037
|
+
description: >-
|
|
3038
|
+
At least one of `notes` or `internal_notes` is required — the API
|
|
3039
|
+
rejects a body carrying neither with a 400. `""` is a meaningful value
|
|
3040
|
+
that clears the field, so the constraint is on presence.
|
|
3041
|
+
# Spelled out as two fully-typed branches rather than the terser
|
|
3042
|
+
# `anyOf: [{required: [notes]}, {required: [internal_notes]}]`, because a
|
|
3043
|
+
# branch carrying only `required` generates as `unknown` — and `T |
|
|
3044
|
+
# unknown` collapses to `unknown`, which accepts everything and is worse
|
|
3045
|
+
# than the unconstrained object. Each branch therefore repeats the
|
|
3046
|
+
# properties so openapi-typescript emits a real discriminating union.
|
|
3047
|
+
#
|
|
3048
|
+
# `minProperties: 1` is deliberately absent: `anyOf` on two named
|
|
3049
|
+
# properties already implies it and is strictly stronger (it also rejects
|
|
3050
|
+
# `{ unrelated_key: 1 }`), so keeping both would be two statements of one
|
|
3051
|
+
# invariant to drift apart.
|
|
3052
|
+
anyOf:
|
|
3053
|
+
- type: object
|
|
3054
|
+
required: [notes]
|
|
3055
|
+
properties:
|
|
3056
|
+
notes:
|
|
3057
|
+
description: Customer-visible note.
|
|
3058
|
+
type: string
|
|
3059
|
+
maxLength: 5000
|
|
3060
|
+
internal_notes:
|
|
3061
|
+
description: Staff-only note.
|
|
3062
|
+
type: string
|
|
3063
|
+
maxLength: 5000
|
|
3064
|
+
- type: object
|
|
3065
|
+
required: [internal_notes]
|
|
3066
|
+
properties:
|
|
3067
|
+
notes:
|
|
3068
|
+
description: Customer-visible note.
|
|
3069
|
+
type: string
|
|
3070
|
+
maxLength: 5000
|
|
3071
|
+
internal_notes:
|
|
3072
|
+
description: Staff-only note.
|
|
3073
|
+
type: string
|
|
3074
|
+
maxLength: 5000
|
|
3075
|
+
CancelBookingInput:
|
|
3076
|
+
type: object
|
|
3077
|
+
properties:
|
|
3078
|
+
reason:
|
|
2050
3079
|
type: string
|
|
2051
|
-
|
|
2052
|
-
|
|
3080
|
+
maxLength: 500
|
|
3081
|
+
RescheduleBookingInput:
|
|
3082
|
+
type: object
|
|
3083
|
+
required: [new_start_ts]
|
|
3084
|
+
properties:
|
|
3085
|
+
new_start_ts:
|
|
3086
|
+
$ref: "#/components/schemas/BookingTimestampInput"
|
|
3087
|
+
new_resource_id:
|
|
2053
3088
|
type: string
|
|
3089
|
+
BookingsPagination:
|
|
3090
|
+
description: >-
|
|
3091
|
+
Pagination for a bookings page. `truncated` is the extra statement:
|
|
3092
|
+
the underlying read is capped, and when the cap binds there are
|
|
3093
|
+
matching bookings no cursor from this call reaches — narrow the window.
|
|
3094
|
+
type: object
|
|
3095
|
+
required: [has_more, next_cursor, truncated]
|
|
3096
|
+
properties:
|
|
3097
|
+
has_more:
|
|
3098
|
+
type: boolean
|
|
3099
|
+
next_cursor:
|
|
3100
|
+
type: [string, "null"]
|
|
3101
|
+
truncated:
|
|
3102
|
+
type: boolean
|
|
2054
3103
|
GdprExport:
|
|
2055
3104
|
type: object
|
|
2056
3105
|
required: [id, request_type, status, submitted_at, completed_at]
|
|
@@ -2743,6 +3792,34 @@ components:
|
|
|
2743
3792
|
$ref: "#/components/schemas/Envelope_DealUpdateResult"
|
|
2744
3793
|
ApiResponse_DealRemoveResult:
|
|
2745
3794
|
$ref: "#/components/schemas/Envelope_DealRemoveResult"
|
|
3795
|
+
ApiResponse_Booking:
|
|
3796
|
+
$ref: "#/components/schemas/Envelope_Booking"
|
|
3797
|
+
ApiResponse_BookingServiceArray:
|
|
3798
|
+
$ref: "#/components/schemas/Envelope_BookingServiceArray"
|
|
3799
|
+
ApiResponse_BookingResourceArray:
|
|
3800
|
+
$ref: "#/components/schemas/Envelope_BookingResourceArray"
|
|
3801
|
+
ApiResponse_BookingSlotArray:
|
|
3802
|
+
$ref: "#/components/schemas/Envelope_BookingSlotArray"
|
|
3803
|
+
ApiResponse_BookingScheduleDayArray:
|
|
3804
|
+
$ref: "#/components/schemas/Envelope_BookingScheduleDayArray"
|
|
3805
|
+
ApiResponse_BookingCreateResult:
|
|
3806
|
+
$ref: "#/components/schemas/Envelope_BookingCreateResult"
|
|
3807
|
+
ApiResponse_BookingActionResult:
|
|
3808
|
+
$ref: "#/components/schemas/Envelope_BookingActionResult"
|
|
3809
|
+
ApiResponse_BookingRescheduleResult:
|
|
3810
|
+
$ref: "#/components/schemas/Envelope_BookingRescheduleResult"
|
|
3811
|
+
ApiResponse_ManageSummary:
|
|
3812
|
+
$ref: "#/components/schemas/Envelope_ManageSummary"
|
|
3813
|
+
ApiResponse_PortalLoginStartResult:
|
|
3814
|
+
$ref: "#/components/schemas/Envelope_PortalLoginStartResult"
|
|
3815
|
+
ApiResponse_PortalSession:
|
|
3816
|
+
$ref: "#/components/schemas/Envelope_PortalSession"
|
|
3817
|
+
ApiResponse_PortalProfile:
|
|
3818
|
+
$ref: "#/components/schemas/Envelope_PortalProfile"
|
|
3819
|
+
ApiResponse_PortalBookings:
|
|
3820
|
+
$ref: "#/components/schemas/Envelope_PortalBookings"
|
|
3821
|
+
ApiResponse_PortalExport:
|
|
3822
|
+
$ref: "#/components/schemas/Envelope_PortalExport"
|
|
2746
3823
|
ApiResponse_GdprExportRequest:
|
|
2747
3824
|
$ref: "#/components/schemas/Envelope_GdprExportRequest"
|
|
2748
3825
|
ApiResponse_GdprExportArray:
|
|
@@ -2832,6 +3909,16 @@ components:
|
|
|
2832
3909
|
$ref: "#/components/schemas/Deal"
|
|
2833
3910
|
pagination:
|
|
2834
3911
|
$ref: "#/components/schemas/Pagination"
|
|
3912
|
+
PaginatedResponse_Booking:
|
|
3913
|
+
type: object
|
|
3914
|
+
required: [data, pagination]
|
|
3915
|
+
properties:
|
|
3916
|
+
data:
|
|
3917
|
+
type: array
|
|
3918
|
+
items:
|
|
3919
|
+
$ref: "#/components/schemas/Booking"
|
|
3920
|
+
pagination:
|
|
3921
|
+
$ref: "#/components/schemas/BookingsPagination"
|
|
2835
3922
|
PaginatedResponse_HelpdeskConversation:
|
|
2836
3923
|
type: object
|
|
2837
3924
|
required: [data, pagination]
|
|
@@ -3002,6 +4089,309 @@ components:
|
|
|
3002
4089
|
properties:
|
|
3003
4090
|
data:
|
|
3004
4091
|
$ref: "#/components/schemas/DealRemoveResult"
|
|
4092
|
+
Envelope_Booking:
|
|
4093
|
+
type: object
|
|
4094
|
+
required: [data]
|
|
4095
|
+
properties:
|
|
4096
|
+
data:
|
|
4097
|
+
$ref: "#/components/schemas/Booking"
|
|
4098
|
+
Envelope_BookingServiceArray:
|
|
4099
|
+
type: object
|
|
4100
|
+
required: [data]
|
|
4101
|
+
properties:
|
|
4102
|
+
data:
|
|
4103
|
+
type: array
|
|
4104
|
+
items:
|
|
4105
|
+
$ref: "#/components/schemas/BookingService"
|
|
4106
|
+
Envelope_BookingResourceArray:
|
|
4107
|
+
type: object
|
|
4108
|
+
required: [data]
|
|
4109
|
+
properties:
|
|
4110
|
+
data:
|
|
4111
|
+
type: array
|
|
4112
|
+
items:
|
|
4113
|
+
$ref: "#/components/schemas/BookingResource"
|
|
4114
|
+
Envelope_BookingSlotArray:
|
|
4115
|
+
type: object
|
|
4116
|
+
required: [data]
|
|
4117
|
+
properties:
|
|
4118
|
+
data:
|
|
4119
|
+
type: array
|
|
4120
|
+
items:
|
|
4121
|
+
$ref: "#/components/schemas/BookingSlot"
|
|
4122
|
+
Envelope_BookingScheduleDayArray:
|
|
4123
|
+
type: object
|
|
4124
|
+
required: [data]
|
|
4125
|
+
properties:
|
|
4126
|
+
data:
|
|
4127
|
+
type: array
|
|
4128
|
+
items:
|
|
4129
|
+
$ref: "#/components/schemas/BookingScheduleDay"
|
|
4130
|
+
Envelope_BookingCreateResult:
|
|
4131
|
+
type: object
|
|
4132
|
+
required: [data]
|
|
4133
|
+
properties:
|
|
4134
|
+
data:
|
|
4135
|
+
$ref: "#/components/schemas/BookingCreateResult"
|
|
4136
|
+
Envelope_BookingActionResult:
|
|
4137
|
+
type: object
|
|
4138
|
+
required: [data]
|
|
4139
|
+
properties:
|
|
4140
|
+
data:
|
|
4141
|
+
$ref: "#/components/schemas/BookingActionResult"
|
|
4142
|
+
Envelope_BookingRescheduleResult:
|
|
4143
|
+
type: object
|
|
4144
|
+
required: [data]
|
|
4145
|
+
properties:
|
|
4146
|
+
data:
|
|
4147
|
+
$ref: "#/components/schemas/BookingRescheduleResult"
|
|
4148
|
+
Envelope_ManageSummary:
|
|
4149
|
+
type: object
|
|
4150
|
+
required: [data]
|
|
4151
|
+
properties:
|
|
4152
|
+
data:
|
|
4153
|
+
$ref: "#/components/schemas/ManageSummary"
|
|
4154
|
+
PortalLoginStartInput:
|
|
4155
|
+
type: object
|
|
4156
|
+
required: [email]
|
|
4157
|
+
properties:
|
|
4158
|
+
email:
|
|
4159
|
+
type: string
|
|
4160
|
+
format: email
|
|
4161
|
+
description: The address the code is sent to.
|
|
4162
|
+
locale:
|
|
4163
|
+
type: string
|
|
4164
|
+
description: Locale for the e-mail (e.g. `nb`, `en`); the workspace default when omitted.
|
|
4165
|
+
PortalLoginStartResult:
|
|
4166
|
+
description: >-
|
|
4167
|
+
Always `sent`, whether or not the address is a known contact — the
|
|
4168
|
+
route is enumeration-safe by design.
|
|
4169
|
+
type: object
|
|
4170
|
+
required: [status]
|
|
4171
|
+
properties:
|
|
4172
|
+
status:
|
|
4173
|
+
type: string
|
|
4174
|
+
const: sent
|
|
4175
|
+
PortalVerifyInput:
|
|
4176
|
+
type: object
|
|
4177
|
+
required: [email, code]
|
|
4178
|
+
properties:
|
|
4179
|
+
email:
|
|
4180
|
+
type: string
|
|
4181
|
+
format: email
|
|
4182
|
+
code:
|
|
4183
|
+
type: string
|
|
4184
|
+
description: The one-time code from the e-mail.
|
|
4185
|
+
PortalContactSummary:
|
|
4186
|
+
type: object
|
|
4187
|
+
required: [contact_id, first_name]
|
|
4188
|
+
properties:
|
|
4189
|
+
contact_id:
|
|
4190
|
+
type: string
|
|
4191
|
+
first_name:
|
|
4192
|
+
type: [string, "null"]
|
|
4193
|
+
PortalSession:
|
|
4194
|
+
description: >-
|
|
4195
|
+
A portal session. `session_token` is a bearer credential for ONE
|
|
4196
|
+
contact — keep it in an HttpOnly cookie on the site's server and never
|
|
4197
|
+
hand it to the browser.
|
|
4198
|
+
type: object
|
|
4199
|
+
required: [session_token, expires_at, contact]
|
|
4200
|
+
properties:
|
|
4201
|
+
session_token:
|
|
4202
|
+
type: string
|
|
4203
|
+
expires_at:
|
|
4204
|
+
type: integer
|
|
4205
|
+
description: Unix timestamp in milliseconds.
|
|
4206
|
+
contact:
|
|
4207
|
+
$ref: "#/components/schemas/PortalContactSummary"
|
|
4208
|
+
PortalFamilyMember:
|
|
4209
|
+
type: object
|
|
4210
|
+
required: [name, birth_year]
|
|
4211
|
+
properties:
|
|
4212
|
+
name:
|
|
4213
|
+
type: string
|
|
4214
|
+
birth_year:
|
|
4215
|
+
type: integer
|
|
4216
|
+
PortalProfile:
|
|
4217
|
+
type: object
|
|
4218
|
+
required:
|
|
4219
|
+
- contact_id
|
|
4220
|
+
- email
|
|
4221
|
+
- first_name
|
|
4222
|
+
- last_name
|
|
4223
|
+
- phone
|
|
4224
|
+
- family
|
|
4225
|
+
- marketing_consent
|
|
4226
|
+
- created_at
|
|
4227
|
+
properties:
|
|
4228
|
+
contact_id:
|
|
4229
|
+
type: string
|
|
4230
|
+
email:
|
|
4231
|
+
type: string
|
|
4232
|
+
first_name:
|
|
4233
|
+
type: [string, "null"]
|
|
4234
|
+
last_name:
|
|
4235
|
+
type: [string, "null"]
|
|
4236
|
+
phone:
|
|
4237
|
+
type: [string, "null"]
|
|
4238
|
+
family:
|
|
4239
|
+
type: array
|
|
4240
|
+
items:
|
|
4241
|
+
$ref: "#/components/schemas/PortalFamilyMember"
|
|
4242
|
+
marketing_consent:
|
|
4243
|
+
type: boolean
|
|
4244
|
+
created_at:
|
|
4245
|
+
type: integer
|
|
4246
|
+
description: Unix timestamp in milliseconds.
|
|
4247
|
+
PortalProfilePatch:
|
|
4248
|
+
description: Only the supplied fields change.
|
|
4249
|
+
type: object
|
|
4250
|
+
properties:
|
|
4251
|
+
first_name:
|
|
4252
|
+
type: string
|
|
4253
|
+
last_name:
|
|
4254
|
+
type: string
|
|
4255
|
+
phone:
|
|
4256
|
+
type: [string, "null"]
|
|
4257
|
+
description: "`null` clears the number."
|
|
4258
|
+
family:
|
|
4259
|
+
type: array
|
|
4260
|
+
description: Replaces the whole list.
|
|
4261
|
+
items:
|
|
4262
|
+
$ref: "#/components/schemas/PortalFamilyMember"
|
|
4263
|
+
marketing_consent:
|
|
4264
|
+
type: boolean
|
|
4265
|
+
description: Records a `marketing_email` consent change with source `portal`.
|
|
4266
|
+
PortalBooking:
|
|
4267
|
+
type: object
|
|
4268
|
+
required:
|
|
4269
|
+
- booking_id
|
|
4270
|
+
- status
|
|
4271
|
+
- start_ts
|
|
4272
|
+
- end_ts
|
|
4273
|
+
- service_id
|
|
4274
|
+
- service_name
|
|
4275
|
+
- resource_id
|
|
4276
|
+
- resource_name
|
|
4277
|
+
- booked_for_name
|
|
4278
|
+
- amount_ore
|
|
4279
|
+
- notes
|
|
4280
|
+
- manage_token
|
|
4281
|
+
- can_manage
|
|
4282
|
+
properties:
|
|
4283
|
+
booking_id:
|
|
4284
|
+
type: string
|
|
4285
|
+
status:
|
|
4286
|
+
$ref: "#/components/schemas/BookingStatus"
|
|
4287
|
+
start_ts:
|
|
4288
|
+
type: integer
|
|
4289
|
+
description: Unix timestamp in milliseconds.
|
|
4290
|
+
end_ts:
|
|
4291
|
+
type: integer
|
|
4292
|
+
description: Unix timestamp in milliseconds.
|
|
4293
|
+
service_id:
|
|
4294
|
+
type: [string, "null"]
|
|
4295
|
+
service_name:
|
|
4296
|
+
type: [string, "null"]
|
|
4297
|
+
resource_id:
|
|
4298
|
+
type: [string, "null"]
|
|
4299
|
+
resource_name:
|
|
4300
|
+
type: [string, "null"]
|
|
4301
|
+
booked_for_name:
|
|
4302
|
+
type: [string, "null"]
|
|
4303
|
+
amount_ore:
|
|
4304
|
+
type: [integer, "null"]
|
|
4305
|
+
description: Integer øre, or `null` when the service has no price.
|
|
4306
|
+
notes:
|
|
4307
|
+
type: [string, "null"]
|
|
4308
|
+
manage_token:
|
|
4309
|
+
type: [string, "null"]
|
|
4310
|
+
description: >-
|
|
4311
|
+
Present only while the booking is upcoming and manageable; opens
|
|
4312
|
+
the site's manage page.
|
|
4313
|
+
can_manage:
|
|
4314
|
+
type: boolean
|
|
4315
|
+
PortalBookings:
|
|
4316
|
+
type: object
|
|
4317
|
+
required: [upcoming, past]
|
|
4318
|
+
properties:
|
|
4319
|
+
upcoming:
|
|
4320
|
+
type: array
|
|
4321
|
+
items:
|
|
4322
|
+
$ref: "#/components/schemas/PortalBooking"
|
|
4323
|
+
past:
|
|
4324
|
+
type: array
|
|
4325
|
+
items:
|
|
4326
|
+
$ref: "#/components/schemas/PortalBooking"
|
|
4327
|
+
PortalConsentRecord:
|
|
4328
|
+
type: object
|
|
4329
|
+
required: [consent_type, granted, granted_at, revoked_at, source]
|
|
4330
|
+
properties:
|
|
4331
|
+
consent_type:
|
|
4332
|
+
type: string
|
|
4333
|
+
granted:
|
|
4334
|
+
type: boolean
|
|
4335
|
+
granted_at:
|
|
4336
|
+
type: [integer, "null"]
|
|
4337
|
+
description: Unix timestamp in milliseconds, or `null`.
|
|
4338
|
+
revoked_at:
|
|
4339
|
+
type: [integer, "null"]
|
|
4340
|
+
description: Unix timestamp in milliseconds, or `null`.
|
|
4341
|
+
source:
|
|
4342
|
+
type: string
|
|
4343
|
+
PortalExport:
|
|
4344
|
+
description: Everything the workspace holds about the signed-in contact (GDPR Art. 15).
|
|
4345
|
+
type: object
|
|
4346
|
+
required: [exported_at, contact, family, consents, bookings]
|
|
4347
|
+
properties:
|
|
4348
|
+
exported_at:
|
|
4349
|
+
type: integer
|
|
4350
|
+
description: Unix timestamp in milliseconds.
|
|
4351
|
+
contact:
|
|
4352
|
+
$ref: "#/components/schemas/PortalProfile"
|
|
4353
|
+
family:
|
|
4354
|
+
type: array
|
|
4355
|
+
items:
|
|
4356
|
+
$ref: "#/components/schemas/PortalFamilyMember"
|
|
4357
|
+
consents:
|
|
4358
|
+
type: array
|
|
4359
|
+
items:
|
|
4360
|
+
$ref: "#/components/schemas/PortalConsentRecord"
|
|
4361
|
+
bookings:
|
|
4362
|
+
type: array
|
|
4363
|
+
items:
|
|
4364
|
+
$ref: "#/components/schemas/PortalBooking"
|
|
4365
|
+
Envelope_PortalLoginStartResult:
|
|
4366
|
+
type: object
|
|
4367
|
+
required: [data]
|
|
4368
|
+
properties:
|
|
4369
|
+
data:
|
|
4370
|
+
$ref: "#/components/schemas/PortalLoginStartResult"
|
|
4371
|
+
Envelope_PortalSession:
|
|
4372
|
+
type: object
|
|
4373
|
+
required: [data]
|
|
4374
|
+
properties:
|
|
4375
|
+
data:
|
|
4376
|
+
$ref: "#/components/schemas/PortalSession"
|
|
4377
|
+
Envelope_PortalProfile:
|
|
4378
|
+
type: object
|
|
4379
|
+
required: [data]
|
|
4380
|
+
properties:
|
|
4381
|
+
data:
|
|
4382
|
+
$ref: "#/components/schemas/PortalProfile"
|
|
4383
|
+
Envelope_PortalBookings:
|
|
4384
|
+
type: object
|
|
4385
|
+
required: [data]
|
|
4386
|
+
properties:
|
|
4387
|
+
data:
|
|
4388
|
+
$ref: "#/components/schemas/PortalBookings"
|
|
4389
|
+
Envelope_PortalExport:
|
|
4390
|
+
type: object
|
|
4391
|
+
required: [data]
|
|
4392
|
+
properties:
|
|
4393
|
+
data:
|
|
4394
|
+
$ref: "#/components/schemas/PortalExport"
|
|
3005
4395
|
ScanCreateInput:
|
|
3006
4396
|
type: object
|
|
3007
4397
|
description: Provide exactly one of `url`, `orgnr`, or `name`.
|