diff --git a/3.1/json/train-travel.json b/3.1/json/train-travel.json new file mode 100644 index 0000000..28dd626 --- /dev/null +++ b/3.1/json/train-travel.json @@ -0,0 +1,1658 @@ +{ + "openapi": "3.1.0", + "info": { + "title": "Train Travel API", + "description": "API for finding and booking train trips across Europe.", + "version": "1.0.0", + "contact": { + "name": "Train Support", + "url": "https://example.com/support", + "email": "support@example.com" + }, + "license": { + "name": "Creative Commons Attribution-NonCommercial-ShareAlike 4.0 International", + "identifier": "CC-BY-NC-SA-4.0", + "url": "https://github.com/bump-sh-examples/train-travel-api/blob/main/LICENSE_CC-BY-NC-SA-4.0" + } + }, + "x-topics": [ + { + "title": "Getting started", + "content": { + "$ref": "./docs/getting-started.md" + } + } + ], + "servers": [ + { + "url": "https://api.example.com", + "description": "Production" + } + ], + "security": [ + { + "OAuth2": [ + "read" + ] + } + ], + "tags": [ + { + "name": "Stations", + "description": "Find and filter train stations across Europe, including their location and local timezone." + }, + { + "name": "Trips", + "description": "Timetables and routes for train trips between stations, including pricing and availability." + }, + { + "name": "Bookings", + "description": "Create and manage bookings for train trips, including passenger details and optional extras." + }, + { + "name": "Payments", + "description": "Pay for bookings using a card or bank account, and view payment\nstatus and history.\n\n> warn\n> Bookings usually expire within 1 hour so you'll need to make your payment before the expiry date \n" + } + ], + "paths": { + "/stations": { + "get": { + "summary": "Get a list of train stations", + "description": "Returns a list of all train stations in the system.", + "operationId": "get-stations", + "tags": [ + "Stations" + ], + "responses": { + "200": { + "description": "A list of train stations", + "headers": { + "RateLimit": { + "$ref": "#/components/headers/RateLimit" + } + }, + "content": { + "application/json": { + "schema": { + "allOf": [ + { + "$ref": "#/components/schemas/Wrapper-Collection" + }, + { + "properties": { + "data": { + "type": "array", + "items": { + "$ref": "#/components/schemas/Station" + } + } + } + }, + { + "properties": { + "links": { + "allOf": [ + { + "$ref": "#/components/schemas/Links-Self" + }, + { + "$ref": "#/components/schemas/Links-Pagination" + } + ] + } + } + } + ] + }, + "example": { + "data": [ + { + "id": "efdbb9d1-02c2-4bc3-afb7-6788d8782b1e", + "name": "Berlin Hauptbahnhof", + "address": "Invalidenstraße 10557 Berlin, Germany", + "country_code": "DE", + "timezone": "Europe/Berlin" + }, + { + "id": "b2e783e1-c824-4d63-b37a-d8d698862f1d", + "name": "Paris Gare du Nord", + "address": "18 Rue de Dunkerque 75010 Paris, France", + "country_code": "FR", + "timezone": "Europe/Paris" + } + ], + "links": { + "self": "https://api.example.com/stations&page=2", + "next": "https://api.example.com/stations?page=3", + "prev": "https://api.example.com/stations?page=1" + } + } + }, + "application/xml": { + "schema": { + "allOf": [ + { + "$ref": "#/components/schemas/Wrapper-Collection" + }, + { + "properties": { + "data": { + "type": "array", + "xml": { + "name": "stations", + "wrapped": true + }, + "items": { + "$ref": "#/components/schemas/Station" + } + } + } + }, + { + "properties": { + "links": { + "allOf": [ + { + "$ref": "#/components/schemas/Links-Self" + }, + { + "$ref": "#/components/schemas/Links-Pagination" + } + ] + } + } + } + ] + } + } + } + }, + "400": { + "$ref": "#/components/responses/BadRequest" + }, + "401": { + "$ref": "#/components/responses/Unauthorized" + }, + "403": { + "$ref": "#/components/responses/Forbidden" + }, + "429": { + "$ref": "#/components/responses/TooManyRequests" + }, + "500": { + "$ref": "#/components/responses/InternalServerError" + } + } + } + }, + "/trips": { + "get": { + "summary": "Get available train trips", + "description": "Returns a list of available train trips between the specified origin and destination stations on the given date, and allows for filtering by bicycle and dog allowances.", + "operationId": "get-trips", + "tags": [ + "Trips" + ], + "parameters": [ + { + "name": "origin", + "in": "query", + "description": "The ID of the origin station", + "required": true, + "schema": { + "type": "string", + "format": "uuid" + }, + "example": "efdbb9d1-02c2-4bc3-afb7-6788d8782b1e" + }, + { + "name": "destination", + "in": "query", + "description": "The ID of the destination station", + "required": true, + "schema": { + "type": "string", + "format": "uuid" + }, + "example": "b2e783e1-c824-4d63-b37a-d8d698862f1d" + }, + { + "name": "date", + "in": "query", + "description": "The date and time of the trip in ISO 8601 format in origin station's timezone.", + "required": true, + "schema": { + "type": "string", + "format": "date-time" + }, + "example": "2024-02-01T09:00:00Z" + }, + { + "name": "bicycles", + "in": "query", + "description": "Only return trips where bicycles are known to be allowed", + "required": false, + "schema": { + "type": "boolean", + "default": false + } + }, + { + "name": "dogs", + "in": "query", + "description": "Only return trips where dogs are known to be allowed", + "required": false, + "schema": { + "type": "boolean", + "default": false + } + } + ], + "responses": { + "200": { + "description": "A list of available train trips", + "headers": { + "RateLimit": { + "$ref": "#/components/headers/RateLimit" + } + }, + "content": { + "application/json": { + "schema": { + "allOf": [ + { + "$ref": "#/components/schemas/Wrapper-Collection" + }, + { + "properties": { + "data": { + "type": "array", + "items": { + "$ref": "#/components/schemas/Trip" + } + } + } + }, + { + "properties": { + "links": { + "allOf": [ + { + "$ref": "#/components/schemas/Links-Self" + }, + { + "$ref": "#/components/schemas/Links-Pagination" + } + ] + } + } + } + ] + }, + "example": { + "data": [ + { + "id": "ea399ba1-6d95-433f-92d1-83f67b775594", + "origin": "efdbb9d1-02c2-4bc3-afb7-6788d8782b1e", + "destination": "b2e783e1-c824-4d63-b37a-d8d698862f1d", + "departure_time": "2024-02-01T10:00:00Z", + "arrival_time": "2024-02-01T16:00:00Z", + "price": 50, + "operator": "Deutsche Bahn", + "bicycles_allowed": true, + "dogs_allowed": true + }, + { + "id": "4d67459c-af07-40bb-bb12-178dbb88e09f", + "origin": "b2e783e1-c824-4d63-b37a-d8d698862f1d", + "destination": "efdbb9d1-02c2-4bc3-afb7-6788d8782b1e", + "departure_time": "2024-02-01T12:00:00Z", + "arrival_time": "2024-02-01T18:00:00Z", + "price": 50, + "operator": "SNCF", + "bicycles_allowed": true, + "dogs_allowed": true + } + ], + "links": { + "self": "https://api.example.com/trips?origin=efdbb9d1-02c2-4bc3-afb7-6788d8782b1e&destination=b2e783e1-c824-4d63-b37a-d8d698862f1d&date=2024-02-01", + "next": "https://api.example.com/trips?origin=efdbb9d1-02c2-4bc3-afb7-6788d8782b1e&destination=b2e783e1-c824-4d63-b37a-d8d698862f1d&date=2024-02-01&page=2" + } + } + }, + "application/xml": { + "schema": { + "allOf": [ + { + "$ref": "#/components/schemas/Wrapper-Collection" + }, + { + "properties": { + "data": { + "type": "array", + "xml": { + "name": "trips", + "wrapped": true + }, + "items": { + "$ref": "#/components/schemas/Trip" + } + } + } + }, + { + "properties": { + "links": { + "allOf": [ + { + "$ref": "#/components/schemas/Links-Self" + }, + { + "$ref": "#/components/schemas/Links-Pagination" + } + ] + } + } + } + ] + } + } + } + }, + "400": { + "$ref": "#/components/responses/BadRequest" + }, + "401": { + "$ref": "#/components/responses/Unauthorized" + }, + "403": { + "$ref": "#/components/responses/Forbidden" + }, + "429": { + "$ref": "#/components/responses/TooManyRequests" + }, + "500": { + "$ref": "#/components/responses/InternalServerError" + } + } + } + }, + "/bookings": { + "get": { + "operationId": "get-bookings", + "summary": "List existing bookings", + "description": "Returns a list of all trips booking by the authenticated user.", + "tags": [ + "Bookings" + ], + "responses": { + "200": { + "description": "A list of bookings", + "headers": { + "RateLimit": { + "$ref": "#/components/headers/RateLimit" + } + }, + "content": { + "application/json": { + "schema": { + "allOf": [ + { + "$ref": "#/components/schemas/Wrapper-Collection" + }, + { + "properties": { + "data": { + "type": "array", + "items": { + "$ref": "#/components/schemas/Booking" + } + } + } + }, + { + "properties": { + "links": { + "allOf": [ + { + "$ref": "#/components/schemas/Links-Self" + }, + { + "$ref": "#/components/schemas/Links-Pagination" + } + ] + } + } + } + ] + }, + "example": { + "data": [ + { + "id": "efdbb9d1-02c2-4bc3-afb7-6788d8782b1e", + "trip_id": "efdbb9d1-02c2-4bc3-afb7-6788d8782b1e", + "passenger_name": "John Doe", + "has_bicycle": true, + "has_dog": true + }, + { + "id": "b2e783e1-c824-4d63-b37a-d8d698862f1d", + "trip_id": "b2e783e1-c824-4d63-b37a-d8d698862f1d", + "passenger_name": "Jane Smith", + "has_bicycle": false, + "has_dog": false + } + ], + "links": { + "self": "https://api.example.com/bookings", + "next": "https://api.example.com/bookings?page=2" + } + } + }, + "application/xml": { + "schema": { + "allOf": [ + { + "$ref": "#/components/schemas/Wrapper-Collection" + }, + { + "properties": { + "data": { + "type": "array", + "xml": { + "name": "bookings", + "wrapped": true + }, + "items": { + "$ref": "#/components/schemas/Booking" + } + } + } + }, + { + "properties": { + "links": { + "allOf": [ + { + "$ref": "#/components/schemas/Links-Self" + }, + { + "$ref": "#/components/schemas/Links-Pagination" + } + ] + } + } + } + ] + } + } + } + }, + "400": { + "$ref": "#/components/responses/BadRequest" + }, + "401": { + "$ref": "#/components/responses/Unauthorized" + }, + "403": { + "$ref": "#/components/responses/Forbidden" + }, + "429": { + "$ref": "#/components/responses/TooManyRequests" + }, + "500": { + "$ref": "#/components/responses/InternalServerError" + } + } + }, + "post": { + "operationId": "create-booking", + "summary": "Create a booking", + "description": "A booking is a temporary hold on a trip. It is not confirmed until the payment is processed.", + "tags": [ + "Bookings" + ], + "security": [ + { + "OAuth2": [ + "write" + ] + } + ], + "requestBody": { + "required": true, + "content": { + "application/json": { + "schema": { + "$ref": "#/components/schemas/Booking" + } + }, + "application/xml": { + "schema": { + "$ref": "#/components/schemas/Booking" + } + } + } + }, + "responses": { + "201": { + "description": "Booking successful", + "content": { + "application/json": { + "schema": { + "allOf": [ + { + "$ref": "#/components/schemas/Booking" + }, + { + "properties": { + "links": { + "$ref": "#/components/schemas/Links-Self" + } + } + } + ] + }, + "example": { + "id": "efdbb9d1-02c2-4bc3-afb7-6788d8782b1e", + "trip_id": "efdbb9d1-02c2-4bc3-afb7-6788d8782b1e", + "passenger_name": "John Doe", + "has_bicycle": true, + "has_dog": true, + "links": { + "self": "https://api.example.com/bookings/efdbb9d1-02c2-4bc3-afb7-6788d8782b1e" + } + } + }, + "application/xml": { + "schema": { + "allOf": [ + { + "$ref": "#/components/schemas/Booking" + }, + { + "properties": { + "links": { + "$ref": "#/components/schemas/Links-Self" + } + } + } + ] + } + } + } + }, + "400": { + "$ref": "#/components/responses/BadRequest" + }, + "401": { + "$ref": "#/components/responses/Unauthorized" + }, + "404": { + "$ref": "#/components/responses/NotFound" + }, + "409": { + "$ref": "#/components/responses/Conflict" + }, + "429": { + "$ref": "#/components/responses/TooManyRequests" + }, + "500": { + "$ref": "#/components/responses/InternalServerError" + } + } + } + }, + "/bookings/{bookingId}": { + "parameters": [ + { + "name": "bookingId", + "in": "path", + "required": true, + "description": "The ID of the booking to retrieve.", + "schema": { + "type": "string", + "format": "uuid" + }, + "example": "1725ff48-ab45-4bb5-9d02-88745177dedb" + } + ], + "get": { + "summary": "Get a booking", + "description": "Returns the details of a specific booking.", + "operationId": "get-booking", + "tags": [ + "Bookings" + ], + "responses": { + "200": { + "description": "The booking details", + "headers": { + "RateLimit": { + "$ref": "#/components/headers/RateLimit" + } + }, + "content": { + "application/json": { + "schema": { + "allOf": [ + { + "$ref": "#/components/schemas/Booking" + }, + { + "properties": { + "links": { + "$ref": "#/components/schemas/Links-Self" + } + } + } + ] + }, + "example": { + "id": "efdbb9d1-02c2-4bc3-afb7-6788d8782b1e", + "trip_id": "efdbb9d1-02c2-4bc3-afb7-6788d8782b1e", + "passenger_name": "John Doe", + "has_bicycle": true, + "has_dog": true, + "links": { + "self": "https://api.example.com/bookings/1725ff48-ab45-4bb5-9d02-88745177dedb" + } + } + }, + "application/xml": { + "schema": { + "allOf": [ + { + "$ref": "#/components/schemas/Booking" + }, + { + "properties": { + "links": { + "$ref": "#/components/schemas/Links-Self" + } + } + } + ] + } + } + } + }, + "400": { + "$ref": "#/components/responses/BadRequest" + }, + "401": { + "$ref": "#/components/responses/Unauthorized" + }, + "403": { + "$ref": "#/components/responses/Forbidden" + }, + "404": { + "$ref": "#/components/responses/NotFound" + }, + "429": { + "$ref": "#/components/responses/TooManyRequests" + }, + "500": { + "$ref": "#/components/responses/InternalServerError" + } + } + }, + "delete": { + "summary": "Delete a booking", + "description": "Deletes a booking, cancelling the hold on the trip.", + "operationId": "delete-booking", + "security": [ + { + "OAuth2": [ + "write" + ] + } + ], + "tags": [ + "Bookings" + ], + "responses": { + "204": { + "description": "Booking deleted" + }, + "400": { + "$ref": "#/components/responses/BadRequest" + }, + "401": { + "$ref": "#/components/responses/Unauthorized" + }, + "403": { + "$ref": "#/components/responses/Forbidden" + }, + "404": { + "$ref": "#/components/responses/NotFound" + }, + "429": { + "$ref": "#/components/responses/TooManyRequests" + }, + "500": { + "$ref": "#/components/responses/InternalServerError" + } + } + } + }, + "/bookings/{bookingId}/payment": { + "parameters": [ + { + "name": "bookingId", + "in": "path", + "required": true, + "description": "The ID of the booking to pay for.", + "schema": { + "type": "string", + "format": "uuid" + }, + "example": "1725ff48-ab45-4bb5-9d02-88745177dedb" + } + ], + "post": { + "summary": "Pay for a Booking", + "description": "A payment is an attempt to pay for the booking, which will confirm the booking for the user and enable them to get their tickets.", + "operationId": "create-booking-payment", + "tags": [ + "Payments" + ], + "requestBody": { + "content": { + "application/json": { + "schema": { + "$ref": "#/components/schemas/BookingPayment" + }, + "examples": { + "Card": { + "summary": "Card Payment", + "value": { + "amount": 49.99, + "currency": "gbp", + "source": { + "object": "card", + "name": "J. Doe", + "number": "4242424242424242", + "cvc": 123, + "exp_month": 12, + "exp_year": 2025, + "address_line1": "123 Fake Street", + "address_line2": "4th Floor", + "address_city": "London", + "address_country": "gb", + "address_post_code": "N12 9XX" + } + } + }, + "Bank": { + "summary": "Bank Account Payment", + "value": { + "amount": 100.5, + "currency": "gbp", + "source": { + "object": "bank_account", + "name": "J. Doe", + "number": "00012345", + "sort_code": "000123", + "account_type": "individual", + "bank_name": "Starling Bank", + "country": "gb" + } + } + } + } + } + } + }, + "responses": { + "200": { + "description": "Payment successful", + "headers": { + "RateLimit": { + "$ref": "#/components/headers/RateLimit" + } + }, + "content": { + "application/json": { + "schema": { + "allOf": [ + { + "$ref": "#/components/schemas/BookingPayment" + }, + { + "properties": { + "links": { + "$ref": "#/components/schemas/Links-Booking" + } + } + } + ] + }, + "examples": { + "Card": { + "summary": "Card Payment", + "value": { + "id": "2e3b4f5a-6b7c-8d9e-0f1a-2b3c4d5e6f7a", + "amount": 49.99, + "currency": "gbp", + "source": { + "object": "card", + "name": "J. Doe", + "number": "************4242", + "cvc": 123, + "exp_month": 12, + "exp_year": 2025, + "address_country": "gb", + "address_post_code": "N12 9XX" + }, + "status": "succeeded", + "links": { + "booking": "https://api.example.com/bookings/1725ff48-ab45-4bb5-9d02-88745177dedb/payment" + } + } + }, + "Bank": { + "summary": "Bank Account Payment", + "value": { + "id": "2e3b4f5a-6b7c-8d9e-0f1a-2b3c4d5e6f7a", + "amount": 100.5, + "currency": "gbp", + "source": { + "object": "bank_account", + "name": "J. Doe", + "account_type": "individual", + "number": "*********2345", + "sort_code": "000123", + "bank_name": "Starling Bank", + "country": "gb" + }, + "status": "succeeded", + "links": { + "booking": "https://api.example.com/bookings/1725ff48-ab45-4bb5-9d02-88745177dedb" + } + } + } + } + } + } + }, + "400": { + "$ref": "#/components/responses/BadRequest" + }, + "401": { + "$ref": "#/components/responses/Unauthorized" + }, + "403": { + "$ref": "#/components/responses/Forbidden" + }, + "429": { + "$ref": "#/components/responses/TooManyRequests" + }, + "500": { + "$ref": "#/components/responses/InternalServerError" + } + } + } + } + }, + "webhooks": { + "newBooking": { + "post": { + "operationId": "new-booking", + "summary": "New Booking", + "description": "Subscribe to new bookings being created, to update integrations for your users. Related data is available via the links provided in the request.\n", + "tags": [ + "Bookings" + ], + "requestBody": { + "content": { + "application/json": { + "schema": { + "allOf": [ + { + "$ref": "#/components/schemas/Booking" + }, + { + "properties": { + "links": { + "allOf": [ + { + "$ref": "#/components/schemas/Links-Self" + }, + { + "$ref": "#/components/schemas/Links-Pagination" + } + ] + } + } + } + ] + }, + "example": { + "id": "efdbb9d1-02c2-4bc3-afb7-6788d8782b1e", + "trip_id": "efdbb9d1-02c2-4bc3-afb7-6788d8782b1e", + "passenger_name": "John Doe", + "has_bicycle": true, + "has_dog": true, + "links": { + "self": "https://api.example.com/bookings/1725ff48-ab45-4bb5-9d02-88745177dedb" + } + } + } + } + }, + "responses": { + "200": { + "description": "Return a 200 status to indicate that the data was received successfully.", + "headers": { + "RateLimit": { + "$ref": "#/components/headers/RateLimit" + } + } + } + } + } + } + }, + "components": { + "securitySchemes": { + "OAuth2": { + "type": "oauth2", + "description": "OAuth 2.0 authorization code following RFC8725 best practices.", + "flows": { + "authorizationCode": { + "authorizationUrl": "https://example.com/oauth/authorize", + "tokenUrl": "https://example.com/oauth/token", + "scopes": { + "read": "Read access", + "write": "Write access" + } + } + } + } + }, + "schemas": { + "Station": { + "type": "object", + "xml": { + "name": "station" + }, + "required": [ + "id", + "name", + "address", + "country_code" + ], + "properties": { + "id": { + "type": "string", + "format": "uuid", + "description": "Unique identifier for the station.", + "examples": [ + "efdbb9d1-02c2-4bc3-afb7-6788d8782b1e", + "b2e783e1-c824-4d63-b37a-d8d698862f1d" + ] + }, + "name": { + "type": "string", + "description": "The name of the station", + "examples": [ + "Berlin Hauptbahnhof", + "Paris Gare du Nord" + ] + }, + "address": { + "type": "string", + "description": "The address of the station.", + "examples": [ + "Invalidenstraße 10557 Berlin, Germany", + "18 Rue de Dunkerque 75010 Paris, France" + ] + }, + "country_code": { + "type": "string", + "description": "The country code of the station.", + "format": "iso-country-code", + "examples": [ + "DE", + "FR" + ] + }, + "timezone": { + "type": "string", + "description": "The timezone of the station in the [IANA Time Zone Database format](https://www.iana.org/time-zones).", + "examples": [ + "Europe/Berlin", + "Europe/Paris" + ] + } + } + }, + "Links-Self": { + "type": "object", + "properties": { + "self": { + "type": "string", + "format": "uri" + } + } + }, + "Links-Pagination": { + "type": "object", + "properties": { + "next": { + "type": "string", + "format": "uri" + }, + "prev": { + "type": "string", + "format": "uri" + } + } + }, + "Problem": { + "xml": { + "name": "problem", + "namespace": "urn:ietf:rfc:7807" + }, + "properties": { + "type": { + "type": "string", + "description": "A URI reference that identifies the problem type", + "example": "https://example.com/probs/out-of-credit" + }, + "title": { + "type": "string", + "description": "A short, human-readable summary of the problem type", + "example": "You do not have enough credit." + }, + "detail": { + "type": "string", + "description": "A human-readable explanation specific to this occurrence of the problem", + "example": "Your current balance is 30, but that costs 50." + }, + "instance": { + "type": "string", + "description": "A URI reference that identifies the specific occurrence of the problem", + "example": "/account/12345/msgs/abc" + }, + "status": { + "type": "integer", + "description": "The HTTP status code", + "example": 400 + } + } + }, + "Trip": { + "type": "object", + "xml": { + "name": "trip" + }, + "properties": { + "id": { + "type": "string", + "format": "uuid", + "description": "Unique identifier for the trip", + "examples": [ + "4f4e4e1-c824-4d63-b37a-d8d698862f1d" + ] + }, + "origin": { + "type": "string", + "description": "The starting station of the trip", + "examples": [ + "Berlin Hauptbahnhof", + "Paris Gare du Nord" + ] + }, + "destination": { + "type": "string", + "description": "The destination station of the trip", + "examples": [ + "Paris Gare du Nord", + "Berlin Hauptbahnhof" + ] + }, + "departure_time": { + "type": "string", + "format": "date-time", + "description": "The date and time when the trip departs", + "examples": [ + "2024-02-01T10:00:00Z" + ] + }, + "arrival_time": { + "type": "string", + "format": "date-time", + "description": "The date and time when the trip arrives", + "examples": [ + "2024-02-01T16:00:00Z" + ] + }, + "operator": { + "type": "string", + "description": "The name of the operator of the trip", + "examples": [ + "Deutsche Bahn", + "SNCF" + ] + }, + "price": { + "type": "number", + "description": "The cost of the trip", + "examples": [ + 50 + ] + }, + "bicycles_allowed": { + "type": "boolean", + "description": "Indicates whether bicycles are allowed on the trip" + }, + "dogs_allowed": { + "type": "boolean", + "description": "Indicates whether dogs are allowed on the trip" + } + } + }, + "Booking": { + "type": "object", + "xml": { + "name": "booking" + }, + "properties": { + "id": { + "type": "string", + "format": "uuid", + "description": "Unique identifier for the booking", + "readOnly": true, + "examples": [ + "3f3e3e1-c824-4d63-b37a-d8d698862f1d" + ] + }, + "trip_id": { + "type": "string", + "format": "uuid", + "description": "Identifier of the booked trip", + "examples": [ + "4f4e4e1-c824-4d63-b37a-d8d698862f1d" + ] + }, + "passenger_name": { + "type": "string", + "description": "Name of the passenger", + "examples": [ + "John Doe" + ] + }, + "has_bicycle": { + "type": "boolean", + "description": "Indicates whether the passenger has a bicycle." + }, + "has_dog": { + "type": "boolean", + "description": "Indicates whether the passenger has a dog." + } + } + }, + "Wrapper-Collection": { + "description": "This is a generic request/response wrapper which contains both data and links which serve as hypermedia controls (HATEOAS).", + "type": "object", + "properties": { + "data": { + "description": "The wrapper for a collection is an array of objects.", + "type": "array", + "items": { + "type": "object" + } + }, + "links": { + "description": "A set of hypermedia links which serve as controls for the client.", + "type": "object", + "readOnly": true + } + }, + "xml": { + "name": "data" + } + }, + "BookingPayment": { + "type": "object", + "properties": { + "id": { + "description": "Unique identifier for the payment. This will be a unique identifier for the payment, and is used to reference the payment in other objects.", + "type": "string", + "format": "uuid", + "readOnly": true + }, + "amount": { + "description": "Amount intended to be collected by this payment. A positive decimal figure describing the amount to be collected.", + "type": "number", + "exclusiveMinimum": 0, + "examples": [ + 49.99 + ] + }, + "currency": { + "description": "Three-letter [ISO currency code](https://www.iso.org/iso-4217-currency-codes.html), in lowercase.", + "type": "string", + "enum": [ + "bam", + "bgn", + "chf", + "eur", + "gbp", + "nok", + "sek", + "try" + ] + }, + "source": { + "unevaluatedProperties": false, + "description": "The payment source to take the payment from. This can be a card or a bank account. Some of these properties will be hidden on read to protect PII leaking.", + "anyOf": [ + { + "title": "Card", + "description": "A card (debit or credit) to take payment from.", + "properties": { + "object": { + "type": "string", + "const": "card" + }, + "name": { + "type": "string", + "description": "Cardholder's full name as it appears on the card.", + "examples": [ + "Francis Bourgeois" + ] + }, + "number": { + "type": "string", + "description": "The card number, as a string without any separators. On read all but the last four digits will be masked for security.", + "examples": [ + "4242424242424242" + ] + }, + "cvc": { + "type": "integer", + "description": "Card security code, 3 or 4 digits usually found on the back of the card.", + "minLength": 3, + "maxLength": 4, + "writeOnly": true, + "example": 123 + }, + "exp_month": { + "type": "integer", + "format": "int64", + "description": "Two-digit number representing the card's expiration month.", + "examples": [ + 12 + ] + }, + "exp_year": { + "type": "integer", + "format": "int64", + "description": "Four-digit number representing the card's expiration year.", + "examples": [ + 2025 + ] + }, + "address_line1": { + "type": "string", + "writeOnly": true + }, + "address_line2": { + "type": "string", + "writeOnly": true + }, + "address_city": { + "type": "string" + }, + "address_country": { + "type": "string" + }, + "address_post_code": { + "type": "string" + } + }, + "required": [ + "name", + "number", + "cvc", + "exp_month", + "exp_year", + "address_country" + ] + }, + { + "title": "Bank Account", + "description": "A bank account to take payment from. Must be able to make payments in the currency specified in the payment.", + "type": "object", + "properties": { + "object": { + "const": "bank_account", + "type": "string" + }, + "name": { + "type": "string" + }, + "number": { + "type": "string", + "description": "The account number for the bank account, in string form. Must be a current account." + }, + "sort_code": { + "type": "string", + "description": "The sort code for the bank account, in string form. Must be a six-digit number." + }, + "account_type": { + "enum": [ + "individual", + "company" + ], + "type": "string", + "description": "The type of entity that holds the account. This can be either `individual` or `company`." + }, + "bank_name": { + "type": "string", + "description": "The name of the bank associated with the routing number.", + "examples": [ + "Starling Bank" + ] + }, + "country": { + "type": "string", + "description": "Two-letter country code (ISO 3166-1 alpha-2)." + } + }, + "required": [ + "name", + "number", + "account_type", + "bank_name", + "country" + ] + } + ] + }, + "status": { + "description": "The status of the payment, one of `pending`, `succeeded`, or `failed`.", + "type": "string", + "enum": [ + "pending", + "succeeded", + "failed" + ], + "readOnly": true + } + } + }, + "Links-Booking": { + "type": "object", + "properties": { + "booking": { + "type": "string", + "format": "uri", + "examples": [ + "https://api.example.com/bookings/1725ff48-ab45-4bb5-9d02-88745177dedb" + ] + } + } + } + }, + "headers": { + "RateLimit": { + "description": "The RateLimit header is communicate quota policies, and contains a `limit` to\nconveys the expiring limit, `remaining` to convey the remaining quota units,\nand `reset` to convey the time window reset time.\n", + "schema": { + "type": "string", + "examples": [ + "limit=10, remaining=0, reset=10" + ] + } + }, + "Retry-After": { + "description": "The Retry-After header indicates how long the user agent should wait before making a follow-up request. \nThe value is in seconds and can be an integer or a date in the future. \nIf the value is an integer, it indicates the number of seconds to wait. \nIf the value is a date, it indicates the time at which the user agent should make a follow-up request. \n", + "schema": { + "type": "string" + }, + "examples": { + "integer": { + "value": "120", + "summary": "Retry after 120 seconds" + }, + "date": { + "value": "Fri, 31 Dec 2021 23:59:59 GMT", + "summary": "Retry after the specified date" + } + } + } + }, + "responses": { + "BadRequest": { + "description": "Bad Request", + "headers": { + "RateLimit": { + "$ref": "#/components/headers/RateLimit" + } + }, + "content": { + "application/problem+json": { + "schema": { + "$ref": "#/components/schemas/Problem" + }, + "example": { + "type": "https://example.com/errors/bad-request", + "title": "Bad Request", + "status": 400, + "detail": "The request is invalid or missing required parameters." + } + }, + "application/problem+xml": { + "schema": { + "$ref": "#/components/schemas/Problem" + }, + "example": { + "type": "https://example.com/errors/bad-request", + "title": "Bad Request", + "status": 400, + "detail": "The request is invalid or missing required parameters." + } + } + } + }, + "Conflict": { + "description": "Conflict", + "headers": { + "RateLimit": { + "$ref": "#/components/headers/RateLimit" + } + }, + "content": { + "application/problem+json": { + "schema": { + "$ref": "#/components/schemas/Problem" + }, + "example": { + "type": "https://example.com/errors/conflict", + "title": "Conflict", + "status": 409, + "detail": "There is a conflict with an existing resource." + } + }, + "application/problem+xml": { + "schema": { + "$ref": "#/components/schemas/Problem" + }, + "example": { + "type": "https://example.com/errors/conflict", + "title": "Conflict", + "status": 409, + "detail": "There is a conflict with an existing resource." + } + } + } + }, + "Forbidden": { + "description": "Forbidden", + "headers": { + "RateLimit": { + "$ref": "#/components/headers/RateLimit" + } + }, + "content": { + "application/problem+json": { + "schema": { + "$ref": "#/components/schemas/Problem" + }, + "example": { + "type": "https://example.com/errors/forbidden", + "title": "Forbidden", + "status": 403, + "detail": "Access is forbidden for the provided credentials." + } + }, + "application/problem+xml": { + "schema": { + "$ref": "#/components/schemas/Problem" + }, + "example": { + "type": "https://example.com/errors/forbidden", + "title": "Forbidden", + "status": 403, + "detail": "Access is forbidden for the provided credentials." + } + } + } + }, + "InternalServerError": { + "description": "Internal Server Error", + "headers": { + "RateLimit": { + "$ref": "#/components/headers/RateLimit" + } + }, + "content": { + "application/problem+json": { + "schema": { + "$ref": "#/components/schemas/Problem" + }, + "example": { + "type": "https://example.com/errors/internal-server-error", + "title": "Internal Server Error", + "status": 500, + "detail": "An unexpected error occurred." + } + }, + "application/problem+xml": { + "schema": { + "$ref": "#/components/schemas/Problem" + }, + "example": { + "type": "https://example.com/errors/internal-server-error", + "title": "Internal Server Error", + "status": 500, + "detail": "An unexpected error occurred." + } + } + } + }, + "NotFound": { + "description": "Not Found", + "headers": { + "RateLimit": { + "$ref": "#/components/headers/RateLimit" + } + }, + "content": { + "application/problem+json": { + "schema": { + "$ref": "#/components/schemas/Problem" + }, + "example": { + "type": "https://example.com/errors/not-found", + "title": "Not Found", + "status": 404, + "detail": "The requested resource was not found." + } + }, + "application/problem+xml": { + "schema": { + "$ref": "#/components/schemas/Problem" + }, + "example": { + "type": "https://example.com/errors/not-found", + "title": "Not Found", + "status": 404, + "detail": "The requested resource was not found." + } + } + } + }, + "TooManyRequests": { + "description": "Too Many Requests", + "headers": { + "RateLimit": { + "$ref": "#/components/headers/RateLimit" + }, + "Retry-After": { + "$ref": "#/components/headers/Retry-After" + } + }, + "content": { + "application/problem+json": { + "schema": { + "$ref": "#/components/schemas/Problem" + }, + "example": { + "type": "https://example.com/errors/too-many-requests", + "title": "Too Many Requests", + "status": 429, + "detail": "You have exceeded the rate limit." + } + }, + "application/problem+xml": { + "schema": { + "$ref": "#/components/schemas/Problem" + }, + "example": { + "type": "https://example.com/errors/too-many-requests", + "title": "Too Many Requests", + "status": 429, + "detail": "You have exceeded the rate limit." + } + } + } + }, + "Unauthorized": { + "description": "Unauthorized", + "headers": { + "RateLimit": { + "$ref": "#/components/headers/RateLimit" + } + }, + "content": { + "application/problem+json": { + "schema": { + "$ref": "#/components/schemas/Problem" + }, + "example": { + "type": "https://example.com/errors/unauthorized", + "title": "Unauthorized", + "status": 401, + "detail": "You do not have the necessary permissions." + } + }, + "application/problem+xml": { + "schema": { + "$ref": "#/components/schemas/Problem" + }, + "example": { + "type": "https://example.com/errors/unauthorized", + "title": "Unauthorized", + "status": 401, + "detail": "You do not have the necessary permissions." + } + } + } + } + } + } +} diff --git a/3.1/yaml/train-travel.yaml b/3.1/yaml/train-travel.yaml new file mode 100644 index 0000000..b37cc93 --- /dev/null +++ b/3.1/yaml/train-travel.yaml @@ -0,0 +1,1100 @@ +openapi: 3.1.0 +info: + title: Train Travel API + description: API for finding and booking train trips across Europe. + version: 1.0.0 + contact: + name: Train Support + url: https://example.com/support + email: support@example.com + license: + name: Creative Commons Attribution-NonCommercial-ShareAlike 4.0 International + identifier: CC-BY-NC-SA-4.0 + url: https://github.com/bump-sh-examples/train-travel-api/blob/main/LICENSE_CC-BY-NC-SA-4.0 + +x-topics: + - title: Getting started + content: + $ref: ./docs/getting-started.md +servers: + - url: https://api.example.com + description: Production +security: + - OAuth2: + - read +tags: + - name: Stations + description: Find and filter train stations across Europe, including their location and local timezone. + - name: Trips + description: Timetables and routes for train trips between stations, including pricing and availability. + - name: Bookings + description: Create and manage bookings for train trips, including passenger details and optional extras. + - name: Payments + description: | + Pay for bookings using a card or bank account, and view payment + status and history. + + > warn + > Bookings usually expire within 1 hour so you'll need to make your payment before the expiry date +paths: + /stations: + get: + summary: Get a list of train stations + description: Returns a list of all train stations in the system. + operationId: get-stations + tags: + - Stations + responses: + '200': + description: A list of train stations + headers: + RateLimit: + $ref: '#/components/headers/RateLimit' + content: + application/json: + schema: + allOf: + - $ref: '#/components/schemas/Wrapper-Collection' + - properties: + data: + type: array + items: + $ref: '#/components/schemas/Station' + - properties: + links: + allOf: + - $ref: '#/components/schemas/Links-Self' + - $ref: '#/components/schemas/Links-Pagination' + example: + data: + - id: efdbb9d1-02c2-4bc3-afb7-6788d8782b1e + name: Berlin Hauptbahnhof + address: Invalidenstraße 10557 Berlin, Germany + country_code: DE + timezone: Europe/Berlin + - id: b2e783e1-c824-4d63-b37a-d8d698862f1d + name: Paris Gare du Nord + address: 18 Rue de Dunkerque 75010 Paris, France + country_code: FR + timezone: Europe/Paris + links: + self: https://api.example.com/stations&page=2 + next: https://api.example.com/stations?page=3 + prev: https://api.example.com/stations?page=1 + application/xml: + schema: + allOf: + - $ref: '#/components/schemas/Wrapper-Collection' + - properties: + data: + type: array + xml: + name: stations + wrapped: true + items: + $ref: '#/components/schemas/Station' + - properties: + links: + allOf: + - $ref: '#/components/schemas/Links-Self' + - $ref: '#/components/schemas/Links-Pagination' + '400': + $ref: '#/components/responses/BadRequest' + '401': + $ref: '#/components/responses/Unauthorized' + '403': + $ref: '#/components/responses/Forbidden' + '429': + $ref: '#/components/responses/TooManyRequests' + '500': + $ref: '#/components/responses/InternalServerError' + /trips: + get: + summary: Get available train trips + description: Returns a list of available train trips between the specified origin and destination stations on the given date, and allows for filtering by bicycle and dog allowances. + operationId: get-trips + tags: + - Trips + parameters: + - name: origin + in: query + description: The ID of the origin station + required: true + schema: + type: string + format: uuid + example: efdbb9d1-02c2-4bc3-afb7-6788d8782b1e + - name: destination + in: query + description: The ID of the destination station + required: true + schema: + type: string + format: uuid + example: b2e783e1-c824-4d63-b37a-d8d698862f1d + - name: date + in: query + description: The date and time of the trip in ISO 8601 format in origin station's timezone. + required: true + schema: + type: string + format: date-time + example: '2024-02-01T09:00:00Z' + - name: bicycles + in: query + description: Only return trips where bicycles are known to be allowed + required: false + schema: + type: boolean + default: false + - name: dogs + in: query + description: Only return trips where dogs are known to be allowed + required: false + schema: + type: boolean + default: false + responses: + '200': + description: A list of available train trips + headers: + RateLimit: + $ref: '#/components/headers/RateLimit' + content: + application/json: + schema: + allOf: + - $ref: '#/components/schemas/Wrapper-Collection' + - properties: + data: + type: array + items: + $ref: '#/components/schemas/Trip' + - properties: + links: + allOf: + - $ref: '#/components/schemas/Links-Self' + - $ref: '#/components/schemas/Links-Pagination' + example: + data: + - id: ea399ba1-6d95-433f-92d1-83f67b775594 + origin: efdbb9d1-02c2-4bc3-afb7-6788d8782b1e + destination: b2e783e1-c824-4d63-b37a-d8d698862f1d + departure_time: '2024-02-01T10:00:00Z' + arrival_time: '2024-02-01T16:00:00Z' + price: 50 + operator: Deutsche Bahn + bicycles_allowed: true + dogs_allowed: true + - id: 4d67459c-af07-40bb-bb12-178dbb88e09f + origin: b2e783e1-c824-4d63-b37a-d8d698862f1d + destination: efdbb9d1-02c2-4bc3-afb7-6788d8782b1e + departure_time: '2024-02-01T12:00:00Z' + arrival_time: '2024-02-01T18:00:00Z' + price: 50 + operator: SNCF + bicycles_allowed: true + dogs_allowed: true + links: + self: https://api.example.com/trips?origin=efdbb9d1-02c2-4bc3-afb7-6788d8782b1e&destination=b2e783e1-c824-4d63-b37a-d8d698862f1d&date=2024-02-01 + next: https://api.example.com/trips?origin=efdbb9d1-02c2-4bc3-afb7-6788d8782b1e&destination=b2e783e1-c824-4d63-b37a-d8d698862f1d&date=2024-02-01&page=2 + application/xml: + schema: + allOf: + - $ref: '#/components/schemas/Wrapper-Collection' + - properties: + data: + type: array + xml: + name: trips + wrapped: true + items: + $ref: '#/components/schemas/Trip' + - properties: + links: + allOf: + - $ref: '#/components/schemas/Links-Self' + - $ref: '#/components/schemas/Links-Pagination' + '400': + $ref: '#/components/responses/BadRequest' + '401': + $ref: '#/components/responses/Unauthorized' + '403': + $ref: '#/components/responses/Forbidden' + '429': + $ref: '#/components/responses/TooManyRequests' + '500': + $ref: '#/components/responses/InternalServerError' + /bookings: + get: + operationId: get-bookings + summary: List existing bookings + description: Returns a list of all trips booking by the authenticated user. + tags: + - Bookings + responses: + '200': + description: A list of bookings + headers: + RateLimit: + $ref: '#/components/headers/RateLimit' + content: + application/json: + schema: + allOf: + - $ref: '#/components/schemas/Wrapper-Collection' + - properties: + data: + type: array + items: + $ref: '#/components/schemas/Booking' + - properties: + links: + allOf: + - $ref: '#/components/schemas/Links-Self' + - $ref: '#/components/schemas/Links-Pagination' + example: + data: + - id: efdbb9d1-02c2-4bc3-afb7-6788d8782b1e + trip_id: efdbb9d1-02c2-4bc3-afb7-6788d8782b1e + passenger_name: John Doe + has_bicycle: true + has_dog: true + - id: b2e783e1-c824-4d63-b37a-d8d698862f1d + trip_id: b2e783e1-c824-4d63-b37a-d8d698862f1d + passenger_name: Jane Smith + has_bicycle: false + has_dog: false + links: + self: https://api.example.com/bookings + next: https://api.example.com/bookings?page=2 + application/xml: + schema: + allOf: + - $ref: '#/components/schemas/Wrapper-Collection' + - properties: + data: + type: array + xml: + name: bookings + wrapped: true + items: + $ref: '#/components/schemas/Booking' + - properties: + links: + allOf: + - $ref: '#/components/schemas/Links-Self' + - $ref: '#/components/schemas/Links-Pagination' + '400': + $ref: '#/components/responses/BadRequest' + '401': + $ref: '#/components/responses/Unauthorized' + '403': + $ref: '#/components/responses/Forbidden' + '429': + $ref: '#/components/responses/TooManyRequests' + '500': + $ref: '#/components/responses/InternalServerError' + post: + operationId: create-booking + summary: Create a booking + description: A booking is a temporary hold on a trip. It is not confirmed until the payment is processed. + tags: + - Bookings + security: + - OAuth2: + - write + requestBody: + required: true + content: + application/json: + schema: + $ref: '#/components/schemas/Booking' + application/xml: + schema: + $ref: '#/components/schemas/Booking' + responses: + '201': + description: Booking successful + content: + application/json: + schema: + allOf: + - $ref: '#/components/schemas/Booking' + - properties: + links: + $ref: '#/components/schemas/Links-Self' + + example: + id: efdbb9d1-02c2-4bc3-afb7-6788d8782b1e + trip_id: efdbb9d1-02c2-4bc3-afb7-6788d8782b1e + passenger_name: John Doe + has_bicycle: true + has_dog: true + links: + self: https://api.example.com/bookings/efdbb9d1-02c2-4bc3-afb7-6788d8782b1e + application/xml: + schema: + allOf: + - $ref: '#/components/schemas/Booking' + - properties: + links: + $ref: '#/components/schemas/Links-Self' + '400': + $ref: '#/components/responses/BadRequest' + '401': + $ref: '#/components/responses/Unauthorized' + '404': + $ref: '#/components/responses/NotFound' + '409': + $ref: '#/components/responses/Conflict' + '429': + $ref: '#/components/responses/TooManyRequests' + '500': + $ref: '#/components/responses/InternalServerError' + + /bookings/{bookingId}: + parameters: + - name: bookingId + in: path + required: true + description: The ID of the booking to retrieve. + schema: + type: string + format: uuid + example: 1725ff48-ab45-4bb5-9d02-88745177dedb + get: + summary: Get a booking + description: Returns the details of a specific booking. + operationId: get-booking + tags: + - Bookings + responses: + '200': + description: The booking details + headers: + RateLimit: + $ref: '#/components/headers/RateLimit' + content: + application/json: + schema: + allOf: + - $ref: '#/components/schemas/Booking' + - properties: + links: + $ref: '#/components/schemas/Links-Self' + example: + id: efdbb9d1-02c2-4bc3-afb7-6788d8782b1e + trip_id: efdbb9d1-02c2-4bc3-afb7-6788d8782b1e + passenger_name: John Doe + has_bicycle: true + has_dog: true + links: + self: https://api.example.com/bookings/1725ff48-ab45-4bb5-9d02-88745177dedb + application/xml: + schema: + allOf: + - $ref: '#/components/schemas/Booking' + - properties: + links: + $ref: '#/components/schemas/Links-Self' + '400': + $ref: '#/components/responses/BadRequest' + '401': + $ref: '#/components/responses/Unauthorized' + '403': + $ref: '#/components/responses/Forbidden' + '404': + $ref: '#/components/responses/NotFound' + '429': + $ref: '#/components/responses/TooManyRequests' + '500': + $ref: '#/components/responses/InternalServerError' + delete: + summary: Delete a booking + description: Deletes a booking, cancelling the hold on the trip. + operationId: delete-booking + security: + - OAuth2: + - write + tags: + - Bookings + responses: + '204': + description: Booking deleted + '400': + $ref: '#/components/responses/BadRequest' + '401': + $ref: '#/components/responses/Unauthorized' + '403': + $ref: '#/components/responses/Forbidden' + '404': + $ref: '#/components/responses/NotFound' + '429': + $ref: '#/components/responses/TooManyRequests' + '500': + $ref: '#/components/responses/InternalServerError' + + /bookings/{bookingId}/payment: + parameters: + - name: bookingId + in: path + required: true + description: The ID of the booking to pay for. + schema: + type: string + format: uuid + example: 1725ff48-ab45-4bb5-9d02-88745177dedb + post: + summary: Pay for a Booking + description: A payment is an attempt to pay for the booking, which will confirm the booking for the user and enable them to get their tickets. + operationId: create-booking-payment + tags: + - Payments + requestBody: + content: + application/json: + schema: + $ref: '#/components/schemas/BookingPayment' + examples: + Card: + summary: Card Payment + value: + amount: 49.99 + currency: gbp + source: + object: card + name: J. Doe + number: '4242424242424242' + cvc: 123 + exp_month: 12 + exp_year: 2025 + address_line1: 123 Fake Street + address_line2: 4th Floor + address_city: London + address_country: gb + address_post_code: N12 9XX + Bank: + summary: Bank Account Payment + value: + amount: 100.5 + currency: gbp + source: + object: bank_account + name: J. Doe + number: '00012345' + sort_code: '000123' + account_type: individual + bank_name: Starling Bank + country: gb + responses: + '200': + description: Payment successful + headers: + RateLimit: + $ref: '#/components/headers/RateLimit' + content: + application/json: + schema: + allOf: + - $ref: '#/components/schemas/BookingPayment' + - properties: + links: + $ref: '#/components/schemas/Links-Booking' + examples: + Card: + summary: Card Payment + value: + id: 2e3b4f5a-6b7c-8d9e-0f1a-2b3c4d5e6f7a + amount: 49.99 + currency: gbp + source: + object: card + name: J. Doe + number: '************4242' + cvc: 123 + exp_month: 12 + exp_year: 2025 + address_country: gb + address_post_code: N12 9XX + status: succeeded + links: + booking: https://api.example.com/bookings/1725ff48-ab45-4bb5-9d02-88745177dedb/payment + Bank: + summary: Bank Account Payment + value: + id: 2e3b4f5a-6b7c-8d9e-0f1a-2b3c4d5e6f7a + amount: 100.5 + currency: gbp + source: + object: bank_account + name: J. Doe + account_type: individual + number: '*********2345' + sort_code: '000123' + bank_name: Starling Bank + country: gb + status: succeeded + links: + booking: https://api.example.com/bookings/1725ff48-ab45-4bb5-9d02-88745177dedb + '400': + $ref: '#/components/responses/BadRequest' + '401': + $ref: '#/components/responses/Unauthorized' + '403': + $ref: '#/components/responses/Forbidden' + '429': + $ref: '#/components/responses/TooManyRequests' + '500': + $ref: '#/components/responses/InternalServerError' +webhooks: + newBooking: + post: + operationId: new-booking + summary: New Booking + description: | + Subscribe to new bookings being created, to update integrations for your users. Related data is available via the links provided in the request. + tags: + - Bookings + requestBody: + content: + application/json: + schema: + allOf: + - $ref: '#/components/schemas/Booking' + - properties: + links: + allOf: + - $ref: '#/components/schemas/Links-Self' + - $ref: '#/components/schemas/Links-Pagination' + example: + id: efdbb9d1-02c2-4bc3-afb7-6788d8782b1e + trip_id: efdbb9d1-02c2-4bc3-afb7-6788d8782b1e + passenger_name: John Doe + has_bicycle: true + has_dog: true + links: + self: https://api.example.com/bookings/1725ff48-ab45-4bb5-9d02-88745177dedb + responses: + '200': + description: Return a 200 status to indicate that the data was received successfully. + headers: + RateLimit: + $ref: '#/components/headers/RateLimit' +components: + securitySchemes: + OAuth2: + type: oauth2 + description: OAuth 2.0 authorization code following RFC8725 best practices. + flows: + authorizationCode: + authorizationUrl: https://example.com/oauth/authorize + tokenUrl: https://example.com/oauth/token + scopes: + read: Read access + write: Write access + schemas: + Station: + type: object + xml: + name: station + required: + - id + - name + - address + - country_code + properties: + id: + type: string + format: uuid + description: Unique identifier for the station. + examples: + - efdbb9d1-02c2-4bc3-afb7-6788d8782b1e + - b2e783e1-c824-4d63-b37a-d8d698862f1d + name: + type: string + description: The name of the station + examples: + - Berlin Hauptbahnhof + - Paris Gare du Nord + address: + type: string + description: The address of the station. + examples: + - Invalidenstraße 10557 Berlin, Germany + - 18 Rue de Dunkerque 75010 Paris, France + country_code: + type: string + description: The country code of the station. + format: iso-country-code + examples: + - DE + - FR + timezone: + type: string + description: The timezone of the station in the [IANA Time Zone Database format](https://www.iana.org/time-zones). + examples: + - Europe/Berlin + - Europe/Paris + Links-Self: + type: object + properties: + self: + type: string + format: uri + Links-Pagination: + type: object + properties: + next: + type: string + format: uri + prev: + type: string + format: uri + Problem: + xml: + name: problem + namespace: urn:ietf:rfc:7807 + properties: + type: + type: string + description: A URI reference that identifies the problem type + example: https://example.com/probs/out-of-credit + title: + type: string + description: A short, human-readable summary of the problem type + example: You do not have enough credit. + detail: + type: string + description: A human-readable explanation specific to this occurrence of the problem + example: Your current balance is 30, but that costs 50. + instance: + type: string + description: A URI reference that identifies the specific occurrence of the problem + example: /account/12345/msgs/abc + status: + type: integer + description: The HTTP status code + example: 400 + Trip: + type: object + xml: + name: trip + properties: + id: + type: string + format: uuid + description: Unique identifier for the trip + examples: + - 4f4e4e1-c824-4d63-b37a-d8d698862f1d + origin: + type: string + description: The starting station of the trip + examples: + - Berlin Hauptbahnhof + - Paris Gare du Nord + destination: + type: string + description: The destination station of the trip + examples: + - Paris Gare du Nord + - Berlin Hauptbahnhof + departure_time: + type: string + format: date-time + description: The date and time when the trip departs + examples: + - '2024-02-01T10:00:00Z' + arrival_time: + type: string + format: date-time + description: The date and time when the trip arrives + examples: + - '2024-02-01T16:00:00Z' + operator: + type: string + description: The name of the operator of the trip + examples: + - Deutsche Bahn + - SNCF + price: + type: number + description: The cost of the trip + examples: + - 50 + bicycles_allowed: + type: boolean + description: Indicates whether bicycles are allowed on the trip + dogs_allowed: + type: boolean + description: Indicates whether dogs are allowed on the trip + Booking: + type: object + xml: + name: booking + properties: + id: + type: string + format: uuid + description: Unique identifier for the booking + readOnly: true + examples: + - 3f3e3e1-c824-4d63-b37a-d8d698862f1d + trip_id: + type: string + format: uuid + description: Identifier of the booked trip + examples: + - 4f4e4e1-c824-4d63-b37a-d8d698862f1d + passenger_name: + type: string + description: Name of the passenger + examples: + - John Doe + has_bicycle: + type: boolean + description: Indicates whether the passenger has a bicycle. + has_dog: + type: boolean + description: Indicates whether the passenger has a dog. + Wrapper-Collection: + description: This is a generic request/response wrapper which contains both data and links which serve as hypermedia controls (HATEOAS). + type: object + properties: + data: + description: The wrapper for a collection is an array of objects. + type: array + items: + type: object + links: + description: A set of hypermedia links which serve as controls for the client. + type: object + readOnly: true + xml: + name: data + BookingPayment: + type: object + properties: + id: + description: Unique identifier for the payment. This will be a unique identifier for the payment, and is used to reference the payment in other objects. + type: string + format: uuid + readOnly: true + amount: + description: Amount intended to be collected by this payment. A positive decimal figure describing the amount to be collected. + type: number + exclusiveMinimum: 0 + examples: + - 49.99 + currency: + description: Three-letter [ISO currency code](https://www.iso.org/iso-4217-currency-codes.html), in lowercase. + type: string + enum: + - bam + - bgn + - chf + - eur + - gbp + - nok + - sek + - try + source: + unevaluatedProperties: false + description: The payment source to take the payment from. This can be a card or a bank account. Some of these properties will be hidden on read to protect PII leaking. + anyOf: + - title: Card + description: A card (debit or credit) to take payment from. + properties: + object: + type: string + const: card + name: + type: string + description: Cardholder's full name as it appears on the card. + examples: + - Francis Bourgeois + number: + type: string + description: The card number, as a string without any separators. On read all but the last four digits will be masked for security. + examples: + - '4242424242424242' + cvc: + type: integer + description: Card security code, 3 or 4 digits usually found on the back of the card. + minLength: 3 + maxLength: 4 + writeOnly: true + + example: 123 + exp_month: + type: integer + format: int64 + description: Two-digit number representing the card's expiration month. + examples: + - 12 + exp_year: + type: integer + format: int64 + description: Four-digit number representing the card's expiration year. + examples: + - 2025 + address_line1: + type: string + writeOnly: true + address_line2: + type: string + writeOnly: true + address_city: + type: string + address_country: + type: string + address_post_code: + type: string + required: + - name + - number + - cvc + - exp_month + - exp_year + - address_country + - title: Bank Account + description: A bank account to take payment from. Must be able to make payments in the currency specified in the payment. + type: object + properties: + object: + const: bank_account + type: string + name: + type: string + number: + type: string + description: The account number for the bank account, in string form. Must be a current account. + sort_code: + type: string + description: The sort code for the bank account, in string form. Must be a six-digit number. + account_type: + enum: + - individual + - company + type: string + description: The type of entity that holds the account. This can be either `individual` or `company`. + bank_name: + type: string + description: The name of the bank associated with the routing number. + examples: + - Starling Bank + country: + type: string + description: Two-letter country code (ISO 3166-1 alpha-2). + required: + - name + - number + - account_type + - bank_name + - country + status: + description: The status of the payment, one of `pending`, `succeeded`, or `failed`. + type: string + enum: + - pending + - succeeded + - failed + readOnly: true + Links-Booking: + type: object + properties: + booking: + type: string + format: uri + examples: + - https://api.example.com/bookings/1725ff48-ab45-4bb5-9d02-88745177dedb + headers: + RateLimit: + description: | + The RateLimit header is communicate quota policies, and contains a `limit` to + conveys the expiring limit, `remaining` to convey the remaining quota units, + and `reset` to convey the time window reset time. + schema: + type: string + examples: + - limit=10, remaining=0, reset=10 + + Retry-After: + description: | + The Retry-After header indicates how long the user agent should wait before making a follow-up request. + The value is in seconds and can be an integer or a date in the future. + If the value is an integer, it indicates the number of seconds to wait. + If the value is a date, it indicates the time at which the user agent should make a follow-up request. + schema: + type: string + examples: + integer: + value: '120' + summary: Retry after 120 seconds + date: + value: 'Fri, 31 Dec 2021 23:59:59 GMT' + summary: Retry after the specified date + + responses: + BadRequest: + description: Bad Request + headers: + RateLimit: + $ref: '#/components/headers/RateLimit' + content: + application/problem+json: + schema: + $ref: '#/components/schemas/Problem' + example: + type: https://example.com/errors/bad-request + title: Bad Request + status: 400 + detail: The request is invalid or missing required parameters. + application/problem+xml: + schema: + $ref: '#/components/schemas/Problem' + example: + type: https://example.com/errors/bad-request + title: Bad Request + status: 400 + detail: The request is invalid or missing required parameters. + + Conflict: + description: Conflict + headers: + RateLimit: + $ref: '#/components/headers/RateLimit' + content: + application/problem+json: + schema: + $ref: '#/components/schemas/Problem' + example: + type: https://example.com/errors/conflict + title: Conflict + status: 409 + detail: There is a conflict with an existing resource. + application/problem+xml: + schema: + $ref: '#/components/schemas/Problem' + example: + type: https://example.com/errors/conflict + title: Conflict + status: 409 + detail: There is a conflict with an existing resource. + + Forbidden: + description: Forbidden + headers: + RateLimit: + $ref: '#/components/headers/RateLimit' + content: + application/problem+json: + schema: + $ref: '#/components/schemas/Problem' + example: + type: https://example.com/errors/forbidden + title: Forbidden + status: 403 + detail: Access is forbidden for the provided credentials. + application/problem+xml: + schema: + $ref: '#/components/schemas/Problem' + example: + type: https://example.com/errors/forbidden + title: Forbidden + status: 403 + detail: Access is forbidden for the provided credentials. + + InternalServerError: + description: Internal Server Error + headers: + RateLimit: + $ref: '#/components/headers/RateLimit' + content: + application/problem+json: + schema: + $ref: '#/components/schemas/Problem' + example: + type: https://example.com/errors/internal-server-error + title: Internal Server Error + status: 500 + detail: An unexpected error occurred. + application/problem+xml: + schema: + $ref: '#/components/schemas/Problem' + example: + type: https://example.com/errors/internal-server-error + title: Internal Server Error + status: 500 + detail: An unexpected error occurred. + + NotFound: + description: Not Found + headers: + RateLimit: + $ref: '#/components/headers/RateLimit' + content: + application/problem+json: + schema: + $ref: '#/components/schemas/Problem' + example: + type: https://example.com/errors/not-found + title: Not Found + status: 404 + detail: The requested resource was not found. + application/problem+xml: + schema: + $ref: '#/components/schemas/Problem' + example: + type: https://example.com/errors/not-found + title: Not Found + status: 404 + detail: The requested resource was not found. + + TooManyRequests: + description: Too Many Requests + headers: + RateLimit: + $ref: '#/components/headers/RateLimit' + Retry-After: + $ref: '#/components/headers/Retry-After' + content: + application/problem+json: + schema: + $ref: '#/components/schemas/Problem' + example: + type: https://example.com/errors/too-many-requests + title: Too Many Requests + status: 429 + detail: You have exceeded the rate limit. + application/problem+xml: + schema: + $ref: '#/components/schemas/Problem' + example: + type: https://example.com/errors/too-many-requests + title: Too Many Requests + status: 429 + detail: You have exceeded the rate limit. + + Unauthorized: + description: Unauthorized + headers: + RateLimit: + $ref: '#/components/headers/RateLimit' + content: + application/problem+json: + schema: + $ref: '#/components/schemas/Problem' + example: + type: https://example.com/errors/unauthorized + title: Unauthorized + status: 401 + detail: You do not have the necessary permissions. + application/problem+xml: + schema: + $ref: '#/components/schemas/Problem' + example: + type: https://example.com/errors/unauthorized + title: Unauthorized + status: 401 + detail: You do not have the necessary permissions.