NAV

Preparation

Before you can use the go~mus reseller API to create valid orders, you will have to register a user account with an API key. Please talk to your contact person in order to get access.

See detailed documentation on Public API for information on how to request the basic data for events, tours and tickets.

Orders

An order is the final result of a checkout process, e.g. like in normal online shops. Every ticket sale or event booking is wrapped into items in a cart and sent to the server for validation.

The server-side will do a final validation and return whether our order was successful. In a success case a JSON with the valid order will be returned.

Note: the payment handling, including access to third party tools, might differ depending on your API plan.

List of existing orders

Existing orders in your account can be accessed at the orders index endpoint:

GET https://demo.gomus.de/api/v4/orders

curl "https://demo.gomus.de/api/v4/orders"
    -H "Authorization: Bearer meowmeowmeow"

The above command returns JSON structured like this:

{
    "orders": [
        {
            "id": 3944805,
            "token": "Aniq532VCewghH2mUtEnng",
            "is_valid": true,
            "total_price_cents": 4500,
            "comment": null,
            "reference": null,
            "rating": null,
            "invoice": false,
            "created_at": "2016-03-28T22:17:46+02:00",
            "source": "resellershop",
            "items": [ ... ]
        }
  ],
  "meta": {
    "total_count": 1,
    "page": 1,
    "per_page": 25
    }
}

Available parameters:

Response

The JSON response contains a list of existing orders as an array and a meta block.

Details of a single order

GET https://demo.gomus.de/api/v4/orders/:id

curl "https://demo.gomus.de/api/v4/orders/1"
    -H "Authorization: Bearer meowmeowmeow"

The above command returns JSON structured like a single element of the orders array from the order list but with two additional keys:

{
    "order": {
        ...

        "payment_status": 30,
        "billing_address_id": 378542,

        ...
    }

}

Response

Find single order by barcode

GET https://demo.gomus.de/api/v4/orders/barcode/:barcode

curl "https://demo.gomus.de/api/v4/orders/barcode/704323341"
    -H "Authorization: Bearer meowmeowmeow"

The above command returns JSON structured just like the details of a single order.

Barcodes

The barcode may belong to any item that is contained in the order.

Documents

Each order item has a pdf document containing the relevant information according to the item type. Ticket item pdfs contain the tickets: one per page. Event and tour items have a pdf containing the booking confirmation.

These are available to download if the order is_valid and if there is a token present.

Some orders may have an invoice available (indicated by the invoice attribute in the order).

Some orders may have passbooks for tickets available (indicated by the passbooks attribute in the ticket sale in order).

Due to the token confirmation, authentication via header request is not needed for the document requests.

Tickets

GET https://demo.gomus.de/api/v4/orders/:order_id/tickets/:ticket_id.pdf?token=:token

Passbooks

GET https://demo.gomus.de/api/v4/orders/:order_id/tickets/:ticket_id/passbooks/:barcode.pkpass?token=:token

Events

GET https://demo.gomus.de/api/v4/orders/:order_id/events/:event_id.pdf?token=:token

Tours

GET https://demo.gomus.de/api/v4/orders/:order_id/tours/:tour_id.pdf?token=:token

Invoice

GET https://demo.gomus.de/api/v4/orders/:order_id/invoice.pdf?token=:token

Order Items

In both list and detailed view the response contains an array of items. Each item can be one of Ticket, Event or Tour with a slightly different structure.

Each item contains generic attributes like

and the specific attributes block, which is different for every type.

Ticket Item

a single ticket type item in the order items array looks like this:

{
  "id": 52397,
  "type": "Ticket",
  "price_cents": 1400,
  "vat_pct": "0.0",
  "tax_included": true,
  "attributes": {
    "id": 42102,
    "title": "El Siglo de Oro mit Gemäldegalerie regulär",
    "ticket_id": 247,
    "quantity": 1,
    "ticket_type": "time_slot",
    "start_time": "2016-09-14T15:00:00+02:00",
    "location": {
      "name": "Gemäldegalerie",
      "city": "Berlin",
      "country": "Deutschland",
      "street": "Matthäikirchplatz",
      "zip": "10785",
      "latitude": 52.5082,
      "longitude": 13.3673
    },
    "barcodes": [
      {
        "id": 97633,
        "barcode": "4034210297633-5",
        "method": "ean"
      }
    ],
    "after_sale_information": null
  }
}

Attributes

Each ticket type item contains attributes like

plus two attribute blocks for the location and the barcodes.

Location

The location is provided as street adress and geo-coordinates. Mind that most museums have no house number.

Barcodes

Each ticket sale will a have one barcode per quantity. Each barcode can be validated and devalued separately.

The barcodes are supposed to be printed on paper. One barcode per sheet.

Note for tickets to attractions of the “Staatlichen Museen zu Berlin”:

The barcodes need to be encoded as CODE 39, so that they can be scanned. Please pay special attention to the required quiet zone and minimal size requirements. You may find an example printout here.

Event Item

a single event type item in the order items array looks like this:

{
  "id": 52399,
  "type": "Event",
  "price_cents": 2000,
  "vat_pct": "0.0",
  "tax_included": true,
  "attributes": {
    "id": 6127,
    "date_id": 22977,
    "event_id": 101174,
    "start_time": "2016-07-09T14:30:00+02:00",
    "title": "El Siglo de Oro",
    "quantity": 5,
    "location": {
      "name": "Gemäldegalerie",
      "city": "Berlin",
      "country": "Deutschland",
      "street": "Matthäikirchplatz",
      "zip": "10785",
      "latitude": 52.5082,
      "longitude": 13.3673
    }
  }
}

Attributes

Each event type item contains attributes like

plus attribute blocks for the location and quantities, if price configuration is complex (scale prices)

Location

The location is provided as street adress and geo-coordinates. Mind that most museums have no house number.

Tour Item

a single tour type item in the order items array looks like this:

{
  "id": 56398,
  "type": "Tour",
  "price_cents": 9000,
  "vat_pct": "0.0",
  "tax_included": true,
  "attributes": {
    "id": 27079,
    "title": "El Siglo de Oro | Gruppenführung",
    "tour_id": 101173,
    "start_time": "2016-07-24T12:00:00+02:00",
    "participants": 12,
    "language": "Deutsch",
    "age_group": "Erwachsene",
    "prices": [
      {
        "title": "Entgelt",
        "description": "Gruppenpreis",
        "total_price_cents": 9000,
        "quantity": 1,
        "price_cents": 9000
      }
    ],
    "location": {
      "name": "Gemäldegalerie",
      "city": "Berlin",
      "country": "Deutschland",
      "street": "Matthäikirchplatz",
      "zip": "10785",
      "latitude": 52.5082,
      "longitude": 13.3673
    }
  }
}

Attributes

Each event type item contains attributes like

plus attribute blocks for the location and prices

Location

The location is provided as street adress and geo-coordinates. Mind that most museums have no house number.

Prices

The price of a tour booking depends on several pricing factors like surcharges and other fees. The prices array holds all single price items which are summed up to the order items price_cents total.

Equipments

The tour item may contain an additional block for booking equipment at the same start time:

    "equipment": [
      {
        "id": 1,
        "quantity": 3
      }
    ]

Coupon Item

a single coupon type item in the order items array looks like this:

{
  "id": 52394,
  "type": "Coupon",
  "price_cents": 5000,
  "vat_pct": "0.0",
  "tax_included": true,
  "attributes": {
    "id": 42102,
    "title": "Coupon für ALLES",
    "quantity": 1,
    "coupon_id": 4,
    "value_cents": 5000,
    "barcodes": [
      {
        "id": 97633,
        "barcode": "79171892",
        "method": "coupon"
      }
    ],
  }
}

Attributes

Each coupon type item contains attributes like

Please pay special attention to the barcode. The barcode, here “79171892”, is the number that the customer has to enter to use the coupon.

Barcodes

Each coupon sale will a have one barcode per quantity. Each barcode can be validated and devalued separately.

Creating an Order

An order is created by a POST request to the orders endpoint:

POST https://demo.gomus.de/api/v4/orders

There can be only one order per request. One order can contain several items (tickets, tour bookings and event bookings)

A final validation check will be made on the server. Either all order items are bought or none. In case of errors you will get error messages back as a response.

Write definition of order into /tmp/order.json before executing shell command.

curl "https://demo.gomus.de/api/v4/orders"
    -XPOST --data "@/tmp/order.json"
    -H "Content-Type: application/json"
    -H "Authorization: Bearer meowmeowmeow"

The above command assumes the order.json JSON is structured like this:

{
  "comment": "Please send fast!",
  "reference": "bought you by the awesome tourist gmbh!",
  "items": [
    ...
  ],
  "coupons": [...],
  "total": 1400
}

Required attributes

Items

A valid order needs at least one valid item. One item can either be a ticket sale, an event booking or a tour booking.

An item has two major attributes:

Ticket

a single item holding a ticket sale is structured like this:

{
  "type": "Ticket",
  "attributes": {
    "id": 247,
    "quantity": 1,
    "time": "2016-07-07T13:00:00+02:00",
    "reservations": ["8CE0mEgjyAvQB1IbPJ4iyg"],
    "attendees": [
      {
        "name": "Capt'n",
        "surname": "Hook"
      },
      {
        "name": "Peter",
        "surname": "Pan"
      }
    ]
  }
}

A ticket item has a fairly simple structure:

Event

a single item holding an event booking is structured like this:

{
  "type": "Event",
  "attributes": {
    "id": 28227,
    "quantity": 5
  }
}

or with several price options:

{
  "type": "Event",
  "attributes": {
    "id": 28227,
    "quantities": {
      "1": 2,
      "4": 3
    }
  }
}

or with specific seating options:

{
  "type": "Event",
  "attributes": {
    "id": 28227,
    "seats": [
      {
        "seat_id": 22,
        "scale_price_id": 33
      },
      {
        "seat_id": 23,
        "scale_price_id": 33
      }
    ]
  }
}

An event item has a similar structure:

If the event only has one price, the quantity key is used.

However, if the event has scale pricing, then the quantities key is used instead:

If the event contains seatings, the strucuture has to contain info about the specific seats:

Tour

a single item holding a tour booking is structured like this:

{
  "type": "Tour",
  "attributes": {
    "id": 28227,
    "start_time": "2016-07-07T13:00:00+02:00",
    "language_id": 1,
    "age_group_id": 3,
    "quantity": 5,
    "surcharges": {
      "1": 2,
      "4": 3
    }
  }
}

or with several price options:

{
  "type": "Tour",
  "attributes": {
    "id": 28227,
    "start_time": "2016-07-07T13:00:00+02:00",
    "language_id": 1,
    "age_group_id": 3,
    "quantities": {
      "1": 2,
      "4": 3
    },
    "surcharges": {
      "1": 2,
      "4": 3
    }
  }
}

A tour item extends the structure of events:

If the tour only has one price, the quantity key is used.

However, if the tour has scale pricing, then the quantities key is used instead:

Surcharge configuration must be provided if tour is configured with surcharges (see tour section in the public api). The surcharge configuration is validated on server side.

Note: the quantity or sum of quantities represents the number of participants (people in group)

Coupon

a single item holding a coupon sale is structured like this:

{
  "type": "Coupon",
  "attributes": {
    "id": 4,
    "quantity": 1,
  }
}

A coupon item has a fairly simple structure:

Using a Coupon

After a coupon has been bought, the customer may want to use it in the online-shop.

Checking Validity

First the system has to check the coupon number that the customer enters. This is done by calling the coupon sale barcode validation API.

curl "https://demo.gomus.de/api/v4/coupon_sales/barcode/123456"

A successful response will look like

{"coupon_sale":{"id":null, "is_valid":true, "value_cents":3000}}

Note that is_valid is true and that value_cents indicate the remaining amount.

In case the coupon has already totally been used, value_cents will be 0.

{"coupon_sale":{"id":null, "is_valid":true, "value_cents":0}}

Please note that the id attribute is currently always null.

In case the coupon number was not valid, the answer will be HTTP 404 Not Found.

In case the system is receiving too many requests that try to use a coupon, HTTP 429 Too Many Requests will be returned.

Using the Coupon in the Order

When the coupon is valid and the customer wants to use it as payment, the coupon number has to be entered in the payload that is sent with order creation.

...
  "coupons": ["123456"],
...

It is possible to specify more than one coupon.

The system will go through each of the coupons in order, and will deduct the maximum amount possible from each coupon.

In case the order total is less than the coupon value, then only the order amount will be deducted and some amount will remain on the coupon.

Combined Tickets

In special cases a museum may offer a combined ticket that includes multiple sub tickets. The sub tickets may have individual separate time slots.

A public example are the Pergamon + Panorama Tickets by the Staatliche Museen zu Berlin (SMB).

Important restrictions

Example Pergamon + Panorama SMB

The following example uses the public API of smb to show a step by step guide on how to order combined tickets. The following data was taken from the live instance on 1st November 2018. The data may look different at a later date.

1. Query ticket list as usual

One of the available tickets will be the following:

{"id": 514, "title": "Pergamonmuseum + Das Panorama | regulär", "ticket_type": "time_slot", "bookable": true, ...}

GET https://smb.gomus.de/api/v4/tickets?valid_at=2018-11-20

Get a list of tickets. One of the tickets will be Pergamonmuseum + Das Panorama | regulär.

2. Reading details of main ticket

Ticket detail response of a ticket with sub tickets:

{
    ...
    "price_cents": 1900,
    "quota_ids": [98],
    "sub_ticket_ids": [515, 516],
    ...
}

GET https://smb.gomus.de/api/v4/tickets/514

In the ticket details we can see that this ticket has an extra key sub_ticket_ids compared to the standard ticket keys. This field contains 2 ID’s of two sub tickets. The sub tickets will refer to the individual entry locations and will later have two individual time slots.

The main ticket itself contains the price, 19 € in our example.

Also take note that the main ticket also has a quota, in order to buy the ticket, all ticket quotas need be available, main and sub tickets.

3. Reading details of sub tickets

The ticket detail response of the sub tickets:

{"id": 515, "title": "Subticket Pergamonmuseum", "is_sub_ticket": true, "price_cents": 0, "quota_ids": [37], "ticket_type": "time_slot", ...}

{"id": 516, "title": "Subticket Das Panorama", "is_sub_ticket": true, "price_cents": 0, "quota_ids": [99], "ticket_type": "time_slot", ...}

GET https://smb.gomus.de/api/v4/tickets/515 GET https://smb.gomus.de/api/v4/tickets/516

Both sub tickets have no price. The price is handled in the main ticket.

But both tickets reference a quota. Both quotas need to be available.

4. Reading capacities

An example of a capacity response:

{
  "2018-11-20T10:00:00+01:00": 46,
  "2018-11-20T10:30:00+01:00": 46,
  "2018-11-20T11:00:00+01:00": 32,
  "2018-11-20T11:30:00+01:00": 37,
  "2018-11-20T12:00:00+01:00": 37,
  "2018-11-20T12:30:00+01:00": 33,
  "2018-11-20T13:00:00+01:00": 37,
  "2018-11-20T13:30:00+01:00": 35,
  "2018-11-20T14:00:00+01:00": 34,
  "2018-11-20T14:30:00+01:00": 37,
  "2018-11-20T15:00:00+01:00": 35,
  "2018-11-20T15:30:00+01:00": 37,
  "2018-11-20T16:00:00+01:00": 37,
  "2018-11-20T16:30:00+01:00": 37,
  "2018-11-20T17:00:00+01:00": 37
}

GET https://smb.gomus.de/api/v4/tickets/515/capacity?date=2018-11-20 GET https://smb.gomus.de/api/v4/tickets/516/capacity?date=2018-11-20

We assume that you already checked the availability of the main ticket and will only look at the sub tickets in the following explanation.

You will need to select one time slot for the first ticket and another time slot for the second ticket.

For example we could select:

Attention: In SMB the time slots have to be 90 minutes apart.

You are free to decide which ticket should have the first time slot. It might be good to let the customer decide where he wants to go first.

In our example the customer will go to “Pergamonmuseum” first and then to “Das Panorama”.

5. Creating the order

Example ticket order item with sub tickets:

{
  "type": "Ticket",
  "attributes": {
    "id": 514,
    "quantity": 1,
    "time": "2018-11-20T12:30:00+01:00",
    "sub_ticket_times": {
      "515": "2018-11-20T12:30:00+01:00",
      "516": "2018-11-20T14:00:00+01:00"
    }
  }
}

See the sections above for reference on how to create an order.

Here we will detail how the ticket attributes are structured inside an order request when buying a combined ticket.

The ticket item is exactly the same as a normal ticket item, with the addition of the sub_ticket_times hash.

Take note that we

  1. send an attributes hash that references the main ticket.
  2. we need to set a time for the main ticket. It is best practice to set this to the earliest time slot that we use in sub tickets.
  3. we set the times for the sub tickets in the field sub_ticket_times. These are the entry times that we selected in step 4.

You can also set the attendees fields in the attributes hash as normal. We omitted them here for brevity. Please not that reservations are not currently possible.

6. Handling of barcodes in the order result

Example of a barcode returned from the order detail response:

"barcodes": [
    {
        "id": 97633,
        "barcode": "4034210297633-5",
        "method": "ean"
    }
],

After you have created the order you will get back a normal order response as was detailed in the sections above.

Of special notice are two things, first you will get a single barcode for the main ticket. This ticket barcode can be used in all entry locations.

And second, it is best practice to print the individual sub ticket times on the ticket PDF that customers will receive.

The sub ticket times will be checked on entry and customer confusion may result if these are not clearly communicated.

Looking at an online example of combined tickets

You can look at the official online shop implementation of Staatliche Museen zu Berlin in order to better understand how to structure queries and how to implement a UI for time slot selection in combined tickets.

Make sure to select 2018-11-20 or a later date when looking for combined Pergamon + Panorama tickets.

https://shop.smb.museum/#/tickets/list?date=2018-11-20

Ticket Reservations

Tickets can be (short term) reserved for 5 minutes each. The reservation can be updated (and refreshed) for a max of 60 minutes reservation time. Note that the ticket itself must be bookable for the time of reservation.

Creating a reservation

A ticket can be reserved like this:

POST https://demo.gomus.de/api/v4/tickets/reservations

Write definition of order into /tmp/reservations.json before executing shell command.

curl "https://demo.gomus.de/api/v4/tickets/reservations"
    -XPOST --data "@/tmp/reservations.json"
    -H "Content-Type: application/json"
    -H "Authorization: Bearer meowmeowmeow"

The above command assumes the reservations.json JSON is structured like this:

  {
      "reservation":
          {
              "ticket_id": 247,
              "quantity": 5,
              "start_at": "2016-09-25T16:00:00+02:00"
         }

   }

The response will contain the reservation object or an error.

Note:

  {
    "reservation":
        {
            "token":"XBqvPGe09EyXPi4iDC1Mgg",
            "quantity":5,
            "ticket_id":247,
            "start_at":"2016-09-25T16:00:00+02:00",
            "valid_until":"2016-09-02T19:11:20+02:00"
        }
  }

Updating and refreshing a reservation

A ticket reservation can be updated and refreshed like this:

PUT https://demo.gomus.de/api/v4/tickets/reservations/:token

Write definition of order into /tmp/reservation_update.json before executing shell command.

curl "https://demo.gomus.de/api/v4/tickets/reservations/:token"
    -XPUT --data "@/tmp/reservations_update.json"
    -H "Content-Type: application/json"
    -H "Authorization: Bearer meowmeowmeow"

The above command assumes the reservation_update.json JSON is structured like this:

{

    "reservation":
        {
            "quantity": 10
        }
 }

The response will contain the reservation object or an error.

Note: you can only update the quantity, the validity will automaticaly refresh on each update.

Deleting a reservation

A ticket reservation can be deleted. It is advised that you do so if the reservation is no longer needed.

DELETE https://demo.gomus.de/api/v4/tickets/reservations/:token

Write definition of order into /tmp/reservation_update.json before executing shell command.

curl "https://demo.gomus.de/api/v4/tickets/reservations/:token"
    -XDELETE
    -H "Content-Type: application/json"
    -H "Authorization: Bearer meowmeowmeow"

The response will have no content.

Annual Tickets

After the purchase of annual ticket(s), the customer receives an e-mail with a link to the shop with a token. The order contains annual ticket(s) and each annual ticket has one or more personalization(s). You can personalize with customer and customer_address which are belong to each order or the personalization can reference other customer or none at all.

Find an order

GET https://demo.gomus.de/api/v4/annual/order?token=:token

curl "https://demo.gomus.de/api/v4/annual/order?token=:token"

The above command returns JSON structured like this:

{
  "order": {
    "id": 85,
    "customer": {
      "id": 18,
      "name": "Bärbel",
      "surname": "Monkey",
      "email": "baerbel@giantmonkey.de",
      "salutation_id": 1,
      "addresses": [
        {
          "id": 18,
          "zip": "10119",
          "street": "Brunnstr. 7d",
          "country_id": 60,
          "city": "Berlin"
        }
      ]
    },
    "shipping_address_id": 18,
    "ticket_sales": [
      {
        "id": 51,
        "title": "Jahreskarte",
        "description": "",
        "start_at": "2019-11-16T00:00:10+01:00",
        "sent_at": null,
        "status": "ready_to_send",
        "is_personalizable": true,
        "personalizations": [
          {
            "id": 10,
            "ready": true,
            "customer": {
              "id": 46,
              "name": "Adrian",
              "surname": "Fuhrmann",
              "email": "adrian@giantmonkey.de",
              "salutation_id": 1,
              "addresses": [
                {
                  "id": 46,
                  "zip": "10119",
                  "street": "Brunnenstr. 7D",
                  "country_id": 60,
                  "city": "Berlin"
                }
              ]
            },
            "address_id": 12,
            "dispatch_mode_id": 3
          }
        ]
      }
    ]
  }
}

Response

The JSON response contains a data of an order you requested.

Create new customer

Create new customer and customer_address if the buyer wants to personalize the ticket for another customer.

POST https://demo.gomus.de/api/v4/annual/customers?token=:token

curl "https://demo.gomus.de/api/v4/annual/customers?token=:token"

The above command assumes the JSON is structured like this:

{
  "customer": {
    "name": "Peter",
    "surname": "Pan",
    "email": "peter@giantmonkey.de",
    "customer_salutation_id": 1,
    "addr_city": "Berlin",
    "addr_country_id": 60,
    "addr_street": "Brunnenstraße 7D",
    "addr_zip": "10119"
  }
}

Required attributes

Response

The response of the above request returns JSON structured like this:

{
    "customer": {
        "id": 146,
        "name": "Peter",
        "surname": "Pan",
        "email": "peter@giantmonkey.de",
        "salutation_id": 1,
        "addresses": [
            {
                "id": 151,
                "zip": "10119",
                "street": "Brunnenstraße 7D",
                "country_id": 60,
                "city": "Berlin"
            }
        ],
        "personalization_token": "13fe3a0d-5983-4db5-a316-eef2f0da8603"
    }
}

The JSON response contains a data of the customer you added.

Update personalization

The existing personalization can be updated (the customer and the address can be changed). dispatch_mode_id is also accepted.

PUT https://demo.gomus.de/api/v4/annual/personalizations/:id?token=:token

curl "https://demo.gomus.de/api/v4/annual/personalizations/:id?token=:token"

The above command assumes the JSON is structured like this:

{
  "personalization": {
    "customer_personalization_token": "13fe3a0d-5983-4db5-a316-eef2f0da8603",
    "customer_adress_id": 151
  }
}

Required attributes

Available parameters:

Update a Ticket Sale

The start time (start_at) of the annual ticket can be changed.

PUT https://demo.gomus.de/api/v4/annual/ticket_sales/:id?token=:token

curl "https://demo.gomus.de/api/v4/annual/ticket_sales/:id?token=:token"

The above command assumes the JSON is structured like this:

{
  "ticket_sale": {
    "start_at": "2019-11-12 10:00:00"
  }
}

Required attributes

Available parameters:

Errors

The go~mus API uses the following error codes:

Error Code Meaning
401 Unauthorized – Your API key is wrong
403 Forbidden – The API endpoint requested is for administrators only
404 Not Found – The specified record could not be found
405 Method Not Allowed – You tried to access an API endpoint with an invalid method
422 Unprocessable Entity – The data you have passed cannot be processed
429 Too Many Requests – You’re requesting too much! Slow down!
500 Internal Server Error – We had a problem with our server. Try again later.
503 Service Unavailable – We’re temporarially offline for maintanance. Please try again later.