This document provides a general overview of the Rocket API, describing the overall process and usage to facilitate development and utilization of relevant endpoints for current and potential clients. The documentation aims to cover:
This general documentation does not include specific details about request and response fields, their types, or other technical specifications. For comprehensive information on request structures, response formats, possible error codes or used fields and their data types, refer to the relevant environment API schema. These schemas provide detailed technical information necessary for implementation and integration purposes.
Rocket API is written in GraphQL which allows dynamic fetching or relevant data per client need.
GraphQL is hosted on the following environments:
Each environment comes with Nitro web UI which nicely displays the GraphQL
schema in the docs tab. Nitro web UI is hosted on /graphql, the graphql
endpoint is hosted on /graphql.
Example of an allPlaces query with just a few fields fetched:
{
allPlaces(purpose: ORIGIN)
{
name
placeId
}
}
GraphQL API operates under the same database as the old API so interoping with the old API for a simpler transition is possible.
Authentication is done per-user basis with AWS Cognito. The API supports multiple apps per Cognito user pool. This can be used to have mobile and web for instance to connect to the same user pool with different authentication requirements.
To use Cognito the required SDK needs to be added to the project.
Documentation for integrating Cognito in the project can be access from https://docs.aws.amazon.com/cognito/latest/developerguide/cognito-integrate-apps.html .
Example of SPA consuming cognito:
The cognito pool is created with only email as sign-in option and the default password policy. To create an application on the pool contact us. It is possible to create a public client, confidential client or custom. More can be seen in the cognito docs. Even if the auth is confidential, it would still be used for customer level authentication, not api to api.
For SPA, public auth should be used as not to reveal client secrets to the public.
To authenticate with the API, one must use the bearer authentication and the
IdToken as the token (Cognito will return 3 tokens, Refresh for the refresh
flow, Access not used by rocket and IdToken). In the GUI just select bearer as
authentication and put the IdToken in the field. When doing a raw request set
the authorization header as follows.
Authorization: Bearer <<id_token>>
Rocket logs a request id with its log messages. This helps to match logs of the same request.
Rocket automatically generates a request id for the whole request. It is possible to supply the request id to rocket, so it can be correclated with the fronted.
To supply the request id add the following to the request headers
X-Request-ID: <UUID>
The id must be in UUID/GUID format, example:
X-Request-ID: b73fcad2-6b75-4a6c-a68f-cf8647facd69
If the request id is malformed, Rocket will create a new one internally. It is advisable to create a new ID for each call, as this makes logs easier to group.
Multiple systems connect to the rocket api, as such we have multiple configurations that are loaded per request. The system resolves the correct configuration based on the origin or user credentials. The credentials take priority. If the user credentials match the origin does not matter.
The system has also segregated databases, so it is not possible to access data from a different configuration.
The origins that will be used must be reported to the rocket developers so that they can be added to the list or origins for origin resolution. When using Nitro the origin is set to the default one, to circumvent this, a person must be logged in with the cognito credentials.
To start searching for tickets the client first needs to know which railcards and places are available.
We only support getting all railcards, as it is a rather light endpoint. Note,
as it has no parameters the query railcard is invoked without parenthesis as
it a field and not a method.
Example:
{
railcards{
code
name
}
}
Rocket API supports different railcard applications, depending on selected railcards and number of passengers:
Railcard validation is part of the standard journeySearch validation. Clients
are not required to perform additional validation on their own. However, clients
who prefer to validate applied railcards and passengers independently can do so
by consuming the validateRailcards endpoint. This endpoint indicates whether
the combination of a passenger set and applied railcard(s) is eligible/valid.
Rocket supports a maximum of three different types of railcards per request. For example, multiple SRN, YNG, TST railcards can be applied and cover multiple or all passengers. Only one railcard can be applied per passenger, but it is not necessary that all passengers are covered by railcard(s).
Examples:
| Adults | Children | Railcard #1 | Railcard #1 Count | Railcard #2 | Railcard #2 Count | Railcard #3 | Railcard #3 Count | Valid |
|---|---|---|---|---|---|---|---|---|
| 1 | 0 | YNG | 1 | / | / | / | / | True |
| 0 | 1 | YNG | 1 | / | / | / | / | False |
| 1 | 0 | YNG | 2 | / | / | / | / | False |
| 2 | 0 | YNG | 2 | / | / | / | / | True |
| 2 | 0 | YNG | 1 | SRN | 1 | / | / | True |
| 2 | 0 | YNG | 1 | 2TR | 1 | / | / | False |
| 2 | 1 | YNG | 1 | 2TR | 1 | / | / | False |
| 3 | 0 | YNG | 1 | 2TR | 1 | / | / | True |
| 3 | 1 | YNG | 1 | 2TR | 1 | / | / | True |
| 4 | 3 | YNG | 1 | 2TR | 1 | / | / | True |
| 4 | 3 | YNG | 2 | 2TR | 2 | / | / | False |
| 6 | 3 | YNG | 2 | 2TR | 2 | / | / | True |
| 3 | 0 | YNG | 1 | SRN | 1 | TSU | 1 | True |
| 3 | 1 | YNG | 1 | 2TR | 1 | TSU | 1 | False |
| 4 | 3 | YNG | 2 | SRN | 1 | TSU | 1 | True |
| 4 | 3 | YNG | 2 | SRN | 2 | TSU | 2 | False |
| 6 | 3 | YNG | 2 | 2TR | 2 | TSU | 1 | False |
| 6 | 3 | YNG | 1 | 2TR | 2 | TSU | 1 | True |
There are different endpoints for fetching a place or a list of them, based on the need.
searchablePlaces: Returns only places applicable for journey search. Since
not all places/stations are valid for origin and destination journey search,
this endpoint returns only suitable ones for journeySearch operation.allPlaces: Returns all places, including places which aren’t searchable i.e.
not valid for journeySearch. Those places are usually displayed in the
service itinerary.placeByCrs: Returns a specific place base on provided CRS value, if exists.placeByNlc: Returns a specific place base on provided NLC value, if exists.placeByUic: Returns a specific place base on provided UIC value, if exists.placeById: Returns a specific place base on provided (Rocket) ID value, if
exists.placeQuery: Gets all relevant places, based on any search value (part of the
name, full name, CRS, UIC, NLC or other aliases). Client should define query
purpose (e.g. origin), desired sorting (e.g. relevance) and number of results.If client decides to cache places locally and implemented their own placeQuery
method, then searchablePlaces should be used as not all places from
allPlaces are valid for journeySearch. Please note that some places can
change over time, so it is good to periodically call the endpoint and check.
Daily cache refresh is advised.
NOTE: In order to simplify fetching a specific place we are planning (in the
near future) to ‘merge’ following methods into single one: placeByCrs,
placeByNlc, placeByUic, placeById.
The Rocket API supports three different journey planning-related endpoints:
Initial Journey Search:
journeySearchSubsequent Journey Searches:
journeySearchByIdJourney Details:
journeyDetailsThe journeySearch endpoint serves as the starting point for the journey
planner functionality. It includes various validations and integrations with
systems outside of Rocket.
The following validations are applied before fetching journey planner results:
All validation errors are aggregated and returned in the response at once.
Journey Planner results are obtained based on search validated search query.
Availability check is done against relevant journeys from the step 1
Returning results:
AVAILABILITY_ERROR).* n represents the takeItems value set in the journeySearch request by
the client. Default value is 5. If RARS Broker is used as a fare availability
client, the value is limited to 5 (if bigger, it’ll be defaulted to 5).
takeItems value.journeySearch response includes an error list with a key
AVAILABILITY_ERROR flag and relevant message (e.g., “Fare Availability check
failed. Reservable fares and journeys may be missing or out-of-date”).All journeys with available services and products are returned. If there are no journeys in the response, this could happen due to one or more of the following reasons:
Clients should always check for the error list in the response to handle the
result correctly and inform customers that displayed results might be incomplete
due to an error (e.g., AVAILABILITY_ERROR).
The journeySearchById endpoint returns journey planner results based on the
original searchId. This allows customers to fetch a desired number of records
(determined by client implementation) using pagination. Please note:
Similar to the journeySearch, this endpoint includes integration with the Availability system. However, no validation is needed on the Rocket side (except for the searchId value), and cached journey planner results are fetched (availability checks are always done on demand).
This endpoint typically offers better performance and is strongly recommended for journey search navigation/pagination.
The journeyDetails endpoint returns desired journey details from journey
planner results based on the original searchId.
Background process and behavior are the same as for journeySearchById, with
the addition of extracting only relevant journeys based on the provided
journeyKeys list from the request.
Rocket automatically expires trips based on specific conditions, which also determines when the entire booking expires.
Booking Expiration: The entire booking expires and is automatically deleted when the last remaining trip in the booking expires (either by time or departure).
Booking Expiry Time Calculation: The expiry time for the entire booking is determined by the earliest occurring of these two events across all trips currently in the booking:
flowchart TD
Start([New Trip Added]) --> ClientType{Client Type}
ClientType -->|Public| PublicTime[2-hour Window]
ClientType -->|Corporate| CorpTime[24-hour Window]
PublicTime --> CheckExpire{Expiration<br/>Check}
CorpTime --> CheckExpire
CheckExpire -->|Time Limit Reached| TimeExpire[Trip Expires]
CheckExpire -->|Departure Passed| DepartExpire[Trip Expires]
CheckExpire -->|Still Active| Active[Continue Monitoring]
TimeExpire --> LastTrip{Last Trip<br/>in Booking?}
DepartExpire --> LastTrip
LastTrip -->|Yes| DeleteBooking[Delete Entire Booking]
LastTrip -->|No| RemoveTrip[Remove Trip Only]
RemoveTrip --> UpdateWindow[Update Booking Window]
Active --> UpdateWindow
UpdateWindow --> CheckExpire
%% Styling
classDef clientType fill:#f9f,stroke:#333,color:#000
classDef decision fill:#bbf,stroke:#333,color:#000
classDef timeWindow fill:#dfd,stroke:#333,color:#000
classDef expiration fill:#fdd,stroke:#333,color:#000
classDef activeState fill:#dfd,stroke:#333,color:#000
classDef windowUpdate fill:#ddf,stroke:#333,color:#000
class ClientType clientType
class CheckExpire,LastTrip decision
class PublicTime,CorpTime timeWindow
class TimeExpire,DepartExpire,DeleteBooking expiration
class Active activeState
class UpdateWindow windowUpdate
Initial State:
Ideal: Customer confirms booking within 25 minutes.
Alternative Actions:
Initial State:
Alternative Actions:
The booking system maintains a time-limited window (never longer than 24 hours) that advances only when trips expire based on their creation time, not departure time. Here’s how it works:
When reservations are attached to trips:
While bookings can remain active beyond 24 hours through continuous additions, each individual window never exceeds the time limit. This means:
When a customer adds tickets/fares for a specific journey to the basket, Rocket treats this as adding a trip to a booking. In Rocket terminology, a set of tickets/fares for a specific single/return journey is called a trip, where the basket is considered a booking.
If it’s a customer’s first trip, then a new booking is created by Rocket with the selected trip. From this point on, the client should base all actions on that booking until the booking is confirmed (sale was successful), deleted by the customer, or expired (and consequently deleted automatically by Rocket).
There are following Basket or Trip operations:
addTrip):
bookingId field. This will create a booking with the selected trip.bookingId.seatReservationLevel field indicating the reservation level:
mutation {
addTrip(
input: {
tripInput: {
searchId: "eyJJZCI6IjJkMDYyODdkLTYyNjctNGFhYS04ZWNkLWI0NDE4MGY2M2RlMyIsIklzT3BlblJldHVybiI6ZmFsc2V9"
outboundJourneyKey: "34581"
fareKeys: ["100003"]
}
reservationRequested: true
reservationInput: {
coachType: QUIET
features: NEAR_LUGGAGE_RACK
seatFacingDirection: FORWARD
seatPosition: WINDOW
}
}
) {
addTripResponse {
booking {
id
todCollectionPossibleFromOrigin
trips {
id
}
}
reservationRequested
reservationState
reservationErrors {
key
value
}
}
}
}
{
"data": {
"addTrip": {
"addTripResponse": {
"booking": {
"id": "P7N5DFTKCO",
"todCollectionPossibleFromOrigin": true,
"trips": [
{
"id": "O3KYDALNY8"
}
]
},
"reservationRequested": true,
"reservationState": "SUCCESS",
"reservationErrors": []
}
}
}
}
{
"errors": [
{
"message": "Seat reservations are currently unavailable for this journey. Since reservations are required for this train, we cannot proceed with your booking. Please try searching for an alternative service or departure time.",
"locations": [
{
"line": 2,
"column": 3
}
],
"path": [
"addTrip"
],
"extensions": {
"code": "REQUIRED_RESERVATION_NOT_POSSIBLE"
}
}
],
"data": null
}
{
"data": {
"addTrip": {
"addTripResponse": {
"booking": {
"id": "WSGELGV2S5",
"todCollectionPossibleFromOrigin": true,
"trips": [
{
"id": "K5315OMWTO"
}
]
},
"reservationRequested": false,
"reservationState": "NOT_REQUESTED",
"reservationErrors": []
}
}
}
}
{
"data": {
"addTrip": {
"addTripResponse": {
"booking": {
"id": "FT5KOZYGRS",
"todCollectionPossibleFromOrigin": true,
"trips": [
{
"id": "ODG6U2BNSJ"
}
]
},
"reservationRequested": true,
"reservationState": "SUCCESS",
"reservationErrors": []
}
}
}
}
{
"data": {
"addTrip": {
"addTripResponse": {
"booking": {
"id": "5TBRTZRKUM",
"todCollectionPossibleFromOrigin": true,
"trips": [
{
"id": "ZC7SHSZUU5"
}
]
},
"reservationRequested": true,
"reservationState": "NOT_POSSIBLE",
"reservationErrors": [
{
"key": "NOT_POSSIBLE",
"value": "Reservations are currently not available for this journey. You can still proceed with booking your ticket."
}
]
}
}
}
}
{
"data": {
"addTrip": {
"addTripResponse": {
"booking": {
"id": "K9TNWMN87U",
"todCollectionPossibleFromOrigin": true,
"trips": [
{
"id": "VURGP1A1SR"
}
]
},
"reservationRequested": true,
"reservationState": "FAILED",
"reservationErrors": [
{
"key": "CREATING_RESERVATIONS_FAILED",
"value": "We were unable to complete your reservation request."
}
]
}
}
}
}
addReservation): This endpoint should be used
for cases when related reservations have expired, but customers have requested
reservations or reservations for booking are mandatory. Currently, Rocket uses
NRS Broker, and there are no compelling reasons to use this endpoint - except
as an additional feature for non-mandatory reservations,allowing customers to
request reservations after the initial addTrip process. Reservations made
via NRS Broker expire simultaneously the booking (110 minutes), rendering
functionality for this case obsolete. This endpoint will become necessary when
switching to RARS, as reservations automatically expire after 30 minutes and
customers should have the ability to re-request reservations. Rocket doesn’t
offer specific seat reservations, but customers can set seat preferences. Each
trip has a seatReservationLevel field indicating the reservation level:
deleteTripFromBooking):
deleteTripFromBooking mutation. This will remove the trip from the
booking alongside any reservations if there were any for this trip.deleteBooking):
booking):
pricingBreakdown):
If the user wants to purchase tickets that will be tied to its account and view them, a customer needs to be created for them.
To create a customer, the user must be logged in via cognito. Before any other operations can be performed a customer needs to be created. This can be achieved by calling the CreateCustomer mutation. After this, separate fields can be updated and the whole customer with its cards can be queried.
If the customer wishes to purchase tickets without logging in, a customer does not need to be created, and it cannot be created. The flow is different from the APIv1 flow, where the customer is created in advanced. Here the customer details will just be passed on the anonymous checkout and the customer will be created behind the scenes.
Once the user is happy with the booking, they can proced to purchase it.
To purchase(checkout) the user needs to call either guest checkout or checkout. The payment input object can take multiple different payment options. This is an input union type. Only one can be set. The payment information is detailed in the previous step. Before checking out, the booking will have a list of possible fulfilment types. This can be either eticket or tod. Some bookings will have restrictions that will allow only one type. The user must choose which fulfilment type they want. Etickets are digital, can be either printed by the user or saved on a digital device. ToDs can be collected on Ticket vending machines on stations. Not all stations have the machies and a credit/debit card is required to retrieve them. Whether a statis has a TVM can be found out via the the place endpoints.
If the user wants to get a confirmation email with the the tickets if eticket fulfilment is chosen or the transaction reference in case of a tod, they must select notify during checkout, or they can call notify separatelly later.
The checkout mutation returns the whole booking. If a guest user is doing a guest checkout, they will only be able to get the booking information from this endpoint once the booking is confirmed. It might be a good idea to query as much information as possible and display it to the customer in this step, as the cusotmer might have misstyped their email.
Other restrictions apply to guest customers, a booking can only be queried by a guest customer if it is not confirmed or has no user associated with it. Same applies for adding trips to a booking. A guest customer can only do it, if a booking has no user associated with it. If a user logs in after working on basked, the system will automatically claim the booking as the users on the next operation(GetBooking, AddTrip, AddReservation, DeleteTrip, Checkout).
If a booking was already paid in stripe, the GetStripeSession will return an error, “PAYMENT_ALREADY_PAID”, the frontend should in this case just call checkout. This can happen if some issues occur during Stripe confirmation and checking out.
The system currently does not allow partial refunds, as such the only mutation is RequestRefund, which will request a refund for the whole booking.
We currently support only Stripe. On account payment is only usable with corporate clients that have set up the payment for the company. Mock can only be used on test and staging, it is stripped from the production binaries. It also must be added to the configuration to be able to use.
When paying with the mock, the mock payment must be set to true. Everything else will automatically work.
example:
mutation Checkout(
$bookingId: String!
$fulfilmentType: FulfilmentType!
$ipAddress: String!
$notify: Boolean!
) {
checkout(
input: {
bookingId: $bookingId
fulfilmentType: $fulfilmentType
ipAddress: $ipAddress
notify: $notify
paymentInput: { mock: true }
}
) {
booking {
id
}
}
}
To start working with Stripe we need to provide you with the publishable key, which will be used to display the UI. Contact us to get the key.
You will also need to get the JavaScript library working https://docs.stripe.com/js . We use payment intents as the payment method, all details will be provided via the API.
In order to pay with Stripe a session must be initiated, to create a session call the createStripeSession as shown below.
mutation {
createStripeSession(input: { bookingId: "XYZ" }) {
stripePaymentSession {
customerSecretSession
sessionId
}
}
}
If any parameter of the booking changes the session will not be valid anymore and the backend will reject the payment. To avoid this, call createStripeSession each time a change happens.
If the booking did not change the createStripeSession will return cached data. It is safe to call as many times as needed to render the UI.
After the JS part is loaded and the payment has been accepted, you can call the checkout mutation specifying it is a Stripe payment.
mutation Checkout(
$bookingId: String!
$fulfilmentType: FulfilmentType!
$ipAddress: String!
$notify: Boolean!
) {
checkout(
input: {
bookingId: $bookingId
fulfilmentType: $fulfilmentType
ipAddress: $ipAddress
notify: $notify
paymentInput: { stripe: true }
}
) {
booking {
id
}
}
}
Stripe test cards can be found on https://docs.stripe.com/testing .
To use the on account user, the client must be a corporate client and the company must have the on account payment enabled.
To check if the user’s company has the on account payment enabled call the get company query.
Example:
{
company {
... on Company {
hasOnAccountPayment
}
}
}
{
"data": {
"company": {
"hasOnAccountPayment": true
}
}
}
If the result is true, it is possible to pay with the on account payment method.
On account payment can have an additional fee, this can be seen when calling the organisation query.
{
organisation {
... on Organisation {
onAccountBookingFee
}
}
}
{
"data": {
"organisation": {
"onAccountBookingFee": 3
}
}
}
If onAccountBookingFee has a value, a fee will be added to the payment. The fee will always be bigger than 0 and cannot be zero.
To pay with it, just execute the checkout mutation like the following example shows.
mutation Checkout(
$bookingId: String!
$fulfilmentType: FulfilmentType!
$ipAddress: String!
$notify: Boolean!
) {
checkout(
input: {
bookingId: $bookingId
fulfilmentType: $fulfilmentType
ipAddress: $ipAddress
notify: $notify
paymentInput: { onAccount: true }
}
) {
booking {
id
}
}
}
Travel policies are restrictions on travel that can be enforced on a set of users based on their department or globaly on the company level if the department has no travel policies attached to it. When applying travel policies, we first check the department level, if the department has no travel policies, the company ones are checked, if no policies are defined on the company level, no additional restrictions apply. Company owner role is exempt of Travel policy restrictions, even if restrictions are assigned to the company.
The only restrictions that are always applied on corporate regardles of travel policies or Company Owner overrides are:
The list of all travel policies is available through the company object and ce be fetched via the company endpoints.
All rules in a travel policy are nullable(optional). When a rule is set, the rule will be populated. If the rule is disabled the rule will not be shown. So in short, if the rule is there it exist and it is valid.
Creating travel policies can be done with the ‘addTravelPolicy’ mutation, an example of this is
mutation {
addTravelPolicy(
input: {
companyId: "c63a9754-e905-447e-8806-bebfd2d90603"
name: "Travel Policy Staging 2"
rules: {
firstClassIfCheaperThanStandard: true
fareCap: { priceCap: 100 }
}
}
) {
company {
name
travelPolicies {
id
name
rules {
firstClassAdvanceCheaperThanStandard
firstClassIfWeekend
firstClassIfCheaperThanAnytime
firstClassIfCheaperThanStandard
firstClassIfLongerJourney {
journeyDuration
}
firstClassIfWeekDaysBeforeAfter {
afterTime
beforeTime
}
firstIfPricierPercentage {
percentage
}
fareCap {
priceCap
}
}
}
costCenterId
travelPolicyId
}
errors {
__typename
}
}
}
More complex rules are set by setting their field, simple one are set, by setting their boolean flag to true. This is however true only for adding the travel policy, updating is different.
Updating travel policies is done with updateTravelPolicy mutation. The
mutation input is an input union type or as in the HotChocollate terminology
OneOf. The OneOf allows for only one field to be populated at once, the same
applies to the rules field, which allows to set only one rule.
The rules are a collection of commands to execute on the travel policy. One such example would be
setFareCap:{
priceCap: 200
}
And the inverse
disableFareCap: true
Due to some limitation of the GraphQL standard, a type cannot be declared without a scalar. Because of this simple travel policies need to be enabled with a boolean flag and disabling of policies is done with a boolean flag. Thus, not defining any value(null), no changes are applied to the rule, if a field is set, the command will be issued.
For convenience enabling and updating a rule is merged into a set command, which can be executed for updatading and inserting, thus simplifying the process.
An example of a full request would be.
mutation {
updateTravelPolicy(
companyId: "c63a9754-e905-447e-8806-bebfd2d90603"
id: "7e711792-5dde-43c9-9fe3-6c2119a78953"
input: {
rules: {
firstClassAdvanceCheaperThanStandard: true
}}
) {
company {
name
costCenterId
travelPolicyId
}
errors {
__typename
}
}
}
While we allow only one field to be updated at once, it is possible to batch multiple mutations in one request. This allows the client to either update all fields at once for example with a save changes button or doing it interactively when the user enables/disables a rule.
Deleting a rule can be done by simply calling deleteTravelPolicy An example
would be:
mutation {
deleteTravelPolicy(
input: {
companyId: "c63a9754-e905-447e-8806-bebfd2d90603"
id: "a3bfe93c-69b0-44a7-a2dd-22894b2561a1"
}
) {
company {
name
costCenterId
travelPolicyId
}
errors {
__typename
}
}
}
Users are structured into three groups:
Super admins are the root of the system, they are not part of any entity. They extend org admin permissions by allowing to create and delete organisations and manipulated other super admins.
Org admins are administrator of a giving organisation. They have permissions to manipulate the organisation, to invite/edit/delete other org admins, create/edit/delete companies and invite company owners, which is a role withing corporate users.
Corporate users are part of a different set of permissions than the previously mentioned users. They are always part of a company. Sometimes they can be part of a department. Their role reflects what they can do on the level of company/department/user. All corporate users can book travels, some can book for others and sometimes they can have limitations set upon travel.
A user can invite another user, if the user has permission to invite and if the user has permission on the specific resource.
A user can be invited if it is not already part of any entity or a superadmin on the system. If the user already has a cognito login, the user will just be added. The user will not receive an invite from cognito as it is already there.
If the corporate user has previously been removed, the customer data is still in there and it will be relinked to the user, (bookings etc).
The invite mutations for org admins and corporate users have some parameters set as optional. When a user is inviting another user, and data can be inherited from the context, that data is then inherited and used in the context. An example would be an org admin inviting a company owner. OrganisationId does not need to be supplied as it is inherited from the org admin. The user can provide those parameters, but they must match, for example, a company owner cannot provide a company id that does not match the one he is assigned to. In such cases an error is returned.
To get own user, you must call the user endpoint, this will also give the information of what type of user the logged in user is. This can help to decide how to display the UI.
When querying the system for corporate users via the corporateUsers query, the parameters are implicitly inherited the same way they are in he invite flow. The user can still restrict the search more, but it cannot search outside its scope.
Due to super admin and org admins are being able to invite a company owner, they are able to call the corporateUsers endpoint, but they will only see company owners there.
Updating a corporate user can be done via the updateCorporateUser mutation. A user can update their own user or if permissions allow other users. A super admin or organisation admin can only update a user with the role of Corporate Owner. To update the user not only the role must be right, but also it must belong to the same company/department.
Deleting a user requires permission to delete on that level and require to match the user parameters. It is not possible to delete a lower role from another company. Only from the assigned company. A user also cannot delete itself. Deleting a user does not currently delete the cognito login as it makes it easier to test, it can be implemented later on if needed.
To update an email the user must call the update method for their type SuperAdmin, OrgAdmin or CorporateUser. Setting the email address field with the new email will prompt the user to get and email with a code to their email. The user must then call the udpate method setting the field email address cahnge confirm code with the code received via email. If everything checks out it will change the user email.
The code is valid for one hour. It is not possible to change to an email tha already exists in the system.
A company can be moved only by super admins. A company can be move to another orgnisation of that organiation exists. MoveCompany mutation move the company along with all the users and updates the bookings to show the current organisation.
It is possible for a user that does not exist in the system yet to create its own company and be the company owner of that company. The company will be assigned to the default organisation.
The frontend must register the user via cognito, the user must not be added as a corporate user to another company. Once the user is created, the user must call the publicSignup mutation with all the required information to create the user and the company.