---
openapi: 3.0.1
info:
  title: Mobilitybox API V3
  version: v3
  description: |
    ## The Mobilitybox V3 APIs introduce subscriptions in ticketing functionality.
    <br>
    ### Changes:
    #### Products:
    * `validity_in_minutes` was renamed to `duration_in_minutes`
    * `validity_in_minutes` is deprecated and will be removed in API v4
    * introduction of `duration_definition` (defines how long the ticket is valid)
    * introduction of `is_subscription` (defines if the product is subscribable)

    #### Coupons:
    * add the `subscription` data model
    * reactivate a coupon with a `coupon_reactivation_key` provided by the returned ticket in the activation step
    * add optional activation param `activation_start_datetime` to set the preferred ticket validity start time
    * add `original_coupon_id` and `restored_coupon_id` holding information about if the coupon is restored or was restored and there is a new one

    #### Tickets:
    * introduction of `sold_at`
    * introduction of `coupon_id`
    * introduction of `coupon_reactivation_key`

    #### Subscription:
    * introduction of the Subscription Data Model, which contains status information about the subscription and the subscription cycles
    * a subscription consists of subscription cycles. Each cycle symbolizes an ordered period.
    * added get `/subscriptions/{id}` endpoint
    * added delete `/subscriptions/{id}` (cancel) endpoint
    * added post `/subscriptions/{id}/reorder` endpoint
    * add `original_subscription_id` and `restored_subscription_id` holding information about if the belonging coupon and the subscription was restored

    #### Orders:
    * added post '/orders/{id}/restore' endpoint

    <br>
    ### Important added changes to support ticket price changes (already in v2):

    To support the ticket price coming at the end of the calendar year, we added some new attributes to the products
    in the ticketing API.

    * `recommended_successor_is: "mobilitybox-product-successor_uuid"` holds the product ID of its recommended successor
    * `recommended_successor_of: "mobilitybox-product-predecessor_uuid"` holds the product ID of the product for which this is the recommended successor
    * both are nullable

    Also, a product can now specify its sales period:
    * `sold_from: "2023-01-01T00:00:00Z"` from this time on, it can be ordered
    * `sold_until: "2023-12-31T23:59:59Z"` until this time, it can be ordered

    **Attention**:
    * The ticket price of a product returned from our API will not change. Instead, it will return a new product with the new attributes (like price) and a new product ID. The old product has `recommended_successor_is` set to the new product ID. The new product has `recommended_successor_of` set to the old product ID.
    * The old product will define a `sold_until` timestamp until you can order it with our API.
    A new product can be ordered after its `sold_from` timestamp or when this attribute is null.
    (In test mode, you can also order future products f.e. to test them out)

servers:
  - url: "https://api.themobilitybox.com/v3"

components:
  schemas:
    createOrderResponse:
      type: object
      properties:
        optional_order_reference:
          type: string
          example: "Optional Order Reference"
          description: This is optional the reference that has been put during the order creation process
        order_id:
          type: string
          example: "mobilitybox-order-3b8187d4-d739-4779-b314-e1311994809d"
        restore_secret:
          type: string
          description: This key is used to restore coupons for lost tickets in this order. Save it because it's only returned by the order create endpoint.
          example: "mobilitybox-restore-secret-uuid"
        coupons:
          "$ref": "#/components/schemas/coupons"
    restoreOrderResponse:
      type: object
      properties:
        optional_order_reference:
          type: string
          example: "Optional Order Reference"
          description: This is optional the reference that has been put during the order creation process
        order_id:
          type: string
          example: "mobilitybox-order-3b8187d4-d739-4779-b314-e1311994809d"
        coupons:
          "$ref": "#/components/schemas/restoredCoupons"
    orderResponse:
      type: object
      properties:
        optional_order_reference:
          type: string
          example: "Optional Order Reference"
          description: This is optional the reference that has been put during the order creation process
        order_id:
          type: string
          example: "mobilitybox-order-3b8187d4-d739-4779-b314-e1311994809d"
        coupons:
          "$ref": "#/components/schemas/coupons"
    coupons:
      type: array
      items:
        "$ref": "#/components/schemas/coupon"
    restoredCoupons:
      type: array
      items:
        "$ref": "#/components/schemas/coupon"
      description: New restored Coupons. (only these where the original coupon IDs were passed in the request body)
    coupon:
      type: object
      properties:
        id:
          type: string
          example: "mobilitybox-coupon-909233de-0039-4acf-9530-da4d04c78515"
        product:
          "$ref": "#/components/schemas/product"
        area:
           "$ref": "#/components/schemas/area"
        subscription:
            "$ref": "#/components/schemas/subscription"
        activated:
          type: boolean
          description: "shows if the coupon was activated (for subscription coupons: only for initial activation)"
          example: true
        environment:
          type: string
          example: test
          enum:
            - live
            - test
    products:
      type: array
      items:
        "$ref": "#/components/schemas/product"
    areas:
      type: array
      items:
        "$ref": "#/components/schemas/area"
    product:
      type: object
      properties:
        id:
          type: string
          format: uuid
          example: mobilitybox-product-1b9f706d-5e3f-46f6-908e-a82455960c47
        recommended_successor_is:
          type: string
          format: uuid
          example: mobilitybox-product-uuid
        recommended_successor_of:
          type: string
          format: uuid
          example: mobilitybox-product-uuid
        local_ticket_name:
          type: string
          example: 24StundenTicket 1 Pers. 1a (Handy)
        local_validity_description:
          type: string
          example: Berechtigt zu beliebig häufigen Fahrten in der Startgemeinde. Es
            ist nicht übertragbar und gültig für 24 Stunden.
        area_id:
          type: string
          format: uuid
          example: mobilitybox-area-2b8dc1a8-0506-462f-b319-b8e6b47b424a
        ticket_type:
          type: string
          example: single
          enum:
          - single
          - day
        customer_type:
          type: string
          example: adult
          enum:
          - adult
          - child
          - both
        duration_definition:
          type: string
          example: "duration_in_minutes"
          description: describes the type of validity period of the ticket. For example, duration_in_minutes means the ticket is valid for a specific time in minutes. calendar_month means the ticket is valid for a calendar month, for example, 01.03.2023 to 31.03.2023. Flexible means the ticket is valid for a specific time, e.g., flexible_week from Tuesday to Tuesday next week.
          enum:
            - duration_in_minutes
            - calendar_year
            - calendar_month
            - calendar_weekend
            - calendar_day
            - flexible_year
            - flexible_month
            - flexible_weekend
            - flexible_week
            - flexible_day
        duration_in_minutes:
          type: integer
          example: 1440
        validity_in_minutes:
          type: integer
          example: 1440
          deprecated: true
          description: validity_in_minutes is deprecated in v3 and will be removed in v5, please checkout duration_definition and duration_in_minutes instead
        price_in_cents:
          type: integer
          example: 712
        currency:
          type: string
          example: EUR
        sold_from:
          type: string
          format: date-time
          example: '2022-01-01T00:00:00Z'
          nullable: true
        sold_until:
          type: string
          format: date-time
          example: '2022-12-31T23:59:59Z'
          nullable: true
        identification_medium_schema:
          type: object
          description: |
            This is a [JSON Schema](https://json-schema.org/) that will be used to validate the identification medium when activating a coupon (see coupon activation endpoint)
    ticket:
      type: object
      properties:
        id:
          type: string
          example: mobilitybox-ticket-bac3f6b1-20bf-41b6-8bdc-5068d56364ad
        coupon_id:
          type: string
          example: mobilitybox-coupon-uuid
        coupon_reactivation_key:
          type: string
          example: key
        product:
          "$ref": "#/components/schemas/product"
        area:
          "$ref": "#/components/schemas/area"
        ticket:
          type: object
          properties:
            meta:
              type: object
              properties:
                version:
                  type: string
                  example: "1.0.0"
                requires_engine:
                  type: string
                  example: "1.0"
                template:
                  type: string
                  example: vrs-1
            properties:
              type: object
              properties:
                barcode:
                  type: string
                identification_medium:
                  type: object
                unique_entitlement_id:
                  type: number
                  example: 1234567890
        ticket_created_at:
          type: string
          format: date-time
          example: '2022-12-14T11:00:00Z'
        valid_from:
          type: string
          format: date-time
          example: '2022-12-14T11:00:0Z'
        valid_until:
          type: string
          format: date-time
          example: '2022-12-14T12:30:00Z'
        sold_at:
          type: string
          format: date-time
          example: '2022-12-14T10:00:00Z'
        environment:
          type: string
          example: test
          enum:
          - test
          - live
    area:
      type: object
      properties:
        id:
          type: string
          format: uuid
          example: mobilitybox-area-uuid
        properties:
          type: object
          properties:
            city_name:
              type: string
              example: Example Area Name
            local_zone_name:
              type: string
              example: Example Area Description
        type:
          type: string
          example: Feature
        geometry:
          type: object
          properties:
            type:
              type: string
              example: Polygon
              enum:
              - Polygon
              - MultiPolygon
            coordinates:
              type: array
              items:
                oneOf:
                - type: array
                  items:
                    type: array
                    items:
                      type: array
                      items:
                        type: number
                - type: array
                  items:
                    type: array
                    items:
                      type: array
                      items:
                        type: array
                        items:
                          type: number
                - type: array
                  items:
                    type: array
                    items:
                      type: array
                      items:
                        type: array
                        items:
                          type: number
    subscription:
      type: object
      properties:
        id:
          type: string
          format: uuid
          example: mobilitybox-subscription-uuid
        activate:
          type: boolean
          example: true
        coupon_reactivatable:
          type: boolean
          example: true
        current_cycle_valid_from:
          type: string
          format: date-time
          example: '2023-01-31T23:00:000Z'
        current_cycle_valid_until:
          type: string
          format: date-time
          example: '2022-02-28T22:59:59Z'
        ordered_until:
          type: string
          format: date-time
          example: '2022-03-31T22:59:59Z'
        current_subscription_cycle:
          "$ref": "#/components/schemas/subscription_cycle"
        next_subscription_cycle:
          "$ref": "#/components/schemas/subscription_cycle"
        next_unordered_subscription_cycle:
          "$ref": "#/components/schemas/subscription_cycle"
    subscription_cycle:
      type: object
      properties:
        id:
          type: string
          format: uuid
          example: mobilitybox-cycle-uuid
        valid_from:
          type: string
          format: date-time
          example: '2023-01-31T23:00:000Z'
        valid_until:
          type: string
          format: date-time
          example: '2022-03-31T22:59:59Z'
        ordered:
          type: boolean
          example: false
        coupon_activated:
          type: boolean
          example: false
    api_error:
      type: object
      properties:
        message:
          type: string
    ticket_available_rendering_options:
      type: array
      items:
        type: string
        example: "rendering_engine"
        description: describes the available options for a ticket how it can be rendered
        enum:
          - rendering_engine
          - apple_wallet
          - google_wallet
          - mobility_wallet
  securitySchemes:
    "API-Key via query parameter":
      type: apiKey
      in: query
      name: api_key
    "API-Key via HTTP-Header":
      type: http
      in: header
      scheme: bearer
security:
  - "API-Key via query parameter": []
  - "API-Key via HTTP-Header": []
paths:
  "/ticketing/areas.json":
    get:
      summary: Returns a list of available areas (where products are available).
      operationId: getAreas
      description: |
        Returns a list of available areas (where products are available).
        Contains a [GeoJSON](https://geojson.org/) representation of the geography data.
      tags:
      - Area
      security: []
      responses:
        '200':
          description: success
          content:
            application/json:
              schema:
                "$ref": "#/components/schemas/areas"
  "/ticketing/areas/{id}.json":
    get:
      summary: Retrieve an area by id
      operationId: getAreaById
      description: |
        Returns the details of an area for the id given.
        Contains a [GeoJSON](https://geojson.org/) representation of the geography data.
      tags:
      - Area
      parameters:
        - name: id
          in: path
          required: true
          schema:
            type: string
          description: Unique area id
      security: []
      responses:
        '200':
          description: success
          content:
            application/json:
              schema:
                "$ref": "#/components/schemas/area"
        '404':
          description: Area not found
  "/ticketing/products/search_by_area_id.json":
    get:
      summary: Retrieve all the products searching by area_id
      operationId: searchProductsByAreaId
      tags:
      - Product
      parameters:
        - name: area_id
          in: query
          required: true
          schema:
            type: string
          description: Unique area ID that retrieves only the products available in that area.
      security: []
      responses:
        '200':
          description: success
          content:
            application/json:
              schema:
                "$ref": "#/components/schemas/products"
  "/ticketing/products/search_by_city_name.json":
    get:
      summary: Retrieve all the products searching by city name
      operationId: searchProductsByCityName
      tags:
      - Product
      parameters:
        - name: city_name
          in: query
          required: true
          schema:
            type: string
          example: "Bonn"
          description: City name that retrieves only the products available in that city.
      security: []
      responses:
        '200':
          description: success
          content:
            application/json:
              schema:
                "$ref": "#/components/schemas/products"
  "/ticketing/products/search_by_location.json":
    get:
      summary: Retrieve all the products searching by location (latitude, longitude)
      operationId: searchProductsByLocation
      tags:
      - Product
      parameters:
        - name: latitude
          in: query
          required: true
          schema:
            type: number
          example: 50.733334
          description: Latitude name that is used to retrieve the products.
        - name: longitude
          in: query
          required: true
          schema:
            type: number
          example: 7.100000
          description: Latitude name that is used to retrieve the products.
      security: []
      responses:
        '200':
          description: success
          content:
            application/json:
              schema:
                "$ref": "#/components/schemas/products"
  "/ticketing/products/{id}.json":
    get:
      summary: Retrieve a product by id
      operationId: getProductById
      tags:
      - Product
      parameters:
        - name: id
          in: path
          required: true
          schema:
            type: string
          description: Unique product id
      security: []
      responses:
        '200':
          description: success
          content:
            application/json:
              schema:
                "$ref": "#/components/schemas/product"
        '404':
          description: Product not found
  "/ticketing/orders.json":
    post:
      summary: Create an order for a list of products
      operationId: createOrder
      tags:
      - Order
      requestBody:
        description: |
          To create an order, a list of product IDs is needed (Maximum 10). `optional_order_reference` can be anything
          that you can decide to put as a reference on your side. It will be present inside the order in our systems.
        required: true
        content:
          application/json:
            schema:
              type: object
              properties:
                product_ids:
                  type: array
                  example: ["mobilitybox-product-37ff65b7-081f-46b7-9f39-5fcf68e100e6", "mobilitybox-product-dab84e4a-85d1-4ad6-b7b0-296f34274a20"]
                  minItems: 1
                  maxItems: 10
                optional_order_reference:
                  type: string
                  example: "My reference"
      responses:
        '201':
          description: success
          content:
            application/json:
              schema:
                "$ref": "#/components/schemas/createOrderResponse"
        '400':
          description: Malformed query. Usually, the parameters are not as expected.
        '404':
          description: At least one Product was not Found.
        '422':
          description: Validation of the request body has failed
  "/ticketing/orders/{id}.json":
    get:
      summary: Retrieve an order
      description: |
        Retrieve an order that has been previously created.
      tags:
      - Order
      parameters:
        - in: path
          name: id
          required: true
          schema:
            type: string
          description: Unique order ID - received after creating an order
      security: []
      responses:
        '200':
          description: success
          content:
            application/json:
              schema:
                "$ref": "#/components/schemas/orderResponse"
        '404':
          description: Order not found
  "/ticketing/coupons/{id}.json":
    get:
      summary: Retrieve a coupon
      tags:
      - Coupon
      parameters:
      - in: path
        name: id
        required: true
        schema:
          type: string
        description: Unique coupon ID - received after creating an order
      security: []
      responses:
        '200':
          description: success
          content:
            application/json:
              schema:
                "$ref": "#/components/schemas/coupon"
        '404':
          description: Coupon not found
  "/ticketing/coupons/{id}/activate.json":
    post:
      summary: Activate a coupon
      operationId: activateCoupon
      tags:
      - Coupon
      parameters:
        - in: path
          name: id
          required: true
          schema:
            type: string
          description: Unique coupon ID - received after creating an order
      security: []
      requestBody:
        description: |
          The identification medium of the person activating the coupon. **Needs to be a JSON Object that passes the identification_medium_schema validation inside the ordered product.**

          When you have an active subscription, you can reactivate your coupon with the `coupon_reactivation_key` returned by the ticket. To do so, you must pass it as `reactivation_key` instead of the `identification_medium`.

          Additionally you can pass an `activation_start_datetime` for the initial activation, for example `2023-05-01T00:00:00+02:00`.<br>
          It defines the start when the ticket is valid. The datetime can not be before the coupon order time.
          Activating a coupon with a subscription will automatically set the ticket valid from to the datetime based on the passed `activation_start_datetime` and the `duration_definition` of the product.<br>
          For example, when activating a coupon with `duration_definition="calendar_month"` and passing as `activation_start_datetime="2023-05-15T12:40:00+02:00"`, it will set the ticket subscription start time and ticket valid from to `2023-05-01T00:00:00+02:00`.
        required: true
        content:
          application/json:
            schema:
              oneOf:
                - type: object
                  properties:
                    identification_medium:
                      type: object
                      example: {photo_id_lite: {first_name: "Max", last_name: "Mustermensch", birth_date: "1990-05-30"}}
                    activation_start_datetime:
                      type: string
                      format: date-time
                      example: '2022-01-01T00:00:00Z'
                  required:
                    - "identification_medium"
                - type: object
                  properties:
                    reactivation_key:
                      type: string
                      description: The key returned by ticket API is used to reactivate coupon from a subscription
                  required:
                    - "reactivation_key"
              example:
                identification_medium: {photo_id_lite: {first_name: "Max", last_name: "Mustermensch", birth_date: "1990-05-30"}}
                activation_start_datetime: '2024-04-25T19:10:26+00:00'
      responses:
        '200':
          description: success
          content:
            application/json:
              schema:
                type: object
                properties:
                  ticket_id:
                    type: string
                    example: "mobilitybox-ticket-283037c1-e923-413b-ae86-25194e7f2e65"
        '400':
          description: Malformed query. Usually, the parameters are not as expected.
        '404':
          description: Coupon not found
        '422':
          description: Validation of the request body has failed
  "/ticketing/subscriptions/{id}.json":
    get:
      summary: Retrieve a subscription
      tags:
      - Subscription
      parameters:
      - in: path
        name: id
        required: true
        schema:
          type: string
        description: Unique subscription ID - received after creating an order with a subscribable product
      security: []
      responses:
        '200':
          description: success
          content:
            application/json:
              schema:
                "$ref": "#/components/schemas/subscription"
        '404':
          description: Subscription not found
    delete:
      summary: Cancel a subscription
      description: It sets the `active` of the subscription to false. After you cancel the subscription you can't reorder anymore. For all ordered subscription cycles, you can still receive the tickets.
      tags:
      - Subscription
      parameters:
      - in: path
        name: id
        required: true
        schema:
          type: string
        description: Unique subscription ID - received after creating an order with a subscribable product
      responses:
        '200':
          description: success
          content:
            application/json:
              schema:
                "$ref": "#/components/schemas/subscription"
        '404':
          description: Subscription not found
  "/ticketing/subscriptions/{id}/reorder.json":
    post:
      summary: Reorder a subscription so the coupon can be reactivated again in the next cycle
      operationId: reorderSubscription
      tags:
      - Subscription
      parameters:
      - in: path
        name: id
        required: true
        schema:
          type: string
        description: Unique subscription ID - received after creating an order with a subscribable product
      requestBody:
        description: |
          You can pass a `subscription_cycle_id` to ensure you reorder for a specific cycle.
          When you don't pass a `subscription_cycle_id`, the next unordered subscription cycle will be ordered.
        content:
          application/json:
            schema:
              type: object
              properties:
                subscription_cycle_id:
                  type: string
                  example: "mobilitybox-cycle-uuid"
      responses:
        '200':
          description: success
          content:
            application/json:
              schema:
                "$ref": "#/components/schemas/subscription"
        '400':
          description: Malformed query. Usually, the parameters are not as expected.
        '404':
          description: The subscription was not found.
        '422':
          description: Validation of the request body has failed
  "/ticketing/orders/{id}/restore.json":
    post:
      summary: Restores coupons of an order
      description: |
        Restores all passed coupons of an order and returns new coupons. Also, checkout the [detailed restore ticket explanation](https://developer.themobilitybox.com/ticketing/documentation/restore)
      tags:
      - Order
      - Coupon
      parameters:
        - in: path
          name: id
          required: true
          schema:
            type: string
          description: Unique order ID - received after creating an order
      requestBody:
        description: |
          A list of coupon IDs is needed to restore coupons for an order. Also, you have to pass the `restore_secret` of the order.
        required: true
        content:
          application/json:
            schema:
              type: object
              properties:
                coupon_ids:
                  type: array
                  example: ["mobilitybox-coupon-uuid-1", "mobilitybox-coupon-uuid-2"]
                restore_secret:
                  type: string
                  example: "mobilitybox-restore-secret-uuid"
      responses:
        '200':
          description: |
            success

            Returns the order with a new coupon for each original coupon you passed in the parameters. After that, you can activate the new coupons the usual way by passing the identification medium.
          content:
            application/json:
              schema:
                "$ref": "#/components/schemas/orderResponse"
  "/ticketing/tickets/{id}.json":
    patch:
      summary: Retrieve a ticket only once
      description: Retrieve a ticket by passing the `id`. **Attention! This API call will delete the ticket from our systems once you have received it.**
      operationId: patchTicket
      tags:
      - Ticket
      parameters:
        - in: path
          name: id
          required: true
          schema:
            type: string
          description: Unique ticket ID - received after activating a coupon
      security: []
      responses:
        '200':
          description: success
          content:
            application/json:
              schema:
                "$ref": "#/components/schemas/ticket"
        '202':
          description: A ticket with this ID is not ready yet
          content:
            application/json:
              schema:
                type: object
                example: {"message": "The ticket with this ID is not ready yet. Please try again in a few seconds."}
        '400':
          description: General Error, e.g., wrong API version
        '404':
          description: Ticket not found or already downloaded
          content:
            application/json:
              schema:
                type: object
                example: {"message": "A ticket with this ID has not yet been created or had already been downloaded."}
  "/ticketing/tickets/{ticket_id}/available_rendering_options.json":
    get:
      summary: Gets available rendering options for a given `ticket_id`.
      tags:
      - Ticket
      parameters:
        - in: path
          name: id
          required: true
          schema:
            type: string
          description: Unique ticket ID - received after activating a coupon
      security: []
      responses:
        '200':
          description: success
          content:
            application/json:
              schema:
                "$ref": "#/components/schemas/ticket_available_rendering_options"
        '404':
          description: Ticket not found
          content:
            application/json:
              schema:
                type: object
                example: {"message": "Couldn't find Ticket with 'id'=..."}
