ParkingsAdmin API for channels

(Revised 2021-11-30)

Introduction

Parkingsadmin.com is a SaaS for parkings that manages parking bookings and integrates all the booking channels of a parking in one place. It helps to manage the availability, checkin, reporting and invoicing.

This API is ready for the distribution channels that sell bookings in the name of the parking. Every parking activates a channel in our system to distribute the product and then the Channel is authorized to make reservations through this system. Parkings can also connect to this API to access and manage their bookings.

Format and Versioning

This API is a REST API that speaks JSON. It will always return JSON (unless otherwise stated) and it assumes that information is sent as JSON BODY when possible, besides the query string parameters, of course.

You should include an Accept header including the content type and the version like this:

Accept: application/json; version=1

Timezones

Fields that finish in _at are in UTC time. Fields that finish in _date or _time are in the timezone of the parking. The timezone of the parking is provided in the /parkings endpoint.

Identification and Authentication

All request must include a User-Agent information including the platform and (separated by a space) the client version (not the OS version) that is making the request, for instance:

User-Agent: "channels/ChannelCode 1.0.1"
User-Agent: "parkings/ParkingCode 1.0.1"

You will be provided with the left part of the user agent by us.

Most entry points require authentication. To authenticate your request you must use the "Authorization" HTTP header using as value the token provided by us to every channel or parking operator and prefixing it with the string "Bearer "

Authorization: "Bearer 1234567890abcdef1234567890abcdef"

Errors

The API returns errors using the standard HTTP response codes (see http://www.restapitutorial.com/httpstatuscodes.html). The error also has a JSON response including the code and a message.

Servers

You can use our test server as sandbox with another token that will be provided to you.

The following table shows the location of each endpoint:

Server URL
Sandbox https://api-sandbox.parkingsadmin.com
Production https://api.parkingsadmin.com

Entities summary

Entity Verb Auth Action
/parkings GET Channel,Parking,User Get the list of parkings
/bookings GET Channel,Parking,User List of bookings
/bookings POST Channel,Parking,User Create a new Booking
/bookings/{id} GET Channel,Parking,User View a Booking
/bookings/{id} PUT Channel,Parking,User Modify a Booking
/bookings/{id} DELETE Channel,Parking,User Cancel a Booking
/bookings-available GET Channel,Parking,User Check availability and price

Booking process

BEFORE creating a booking, the channel SHOULD check the availability for that parking and dates.

If you try to create a booking and there is no availability you will get a 409 error anyways.

1) GET /parkings to know our parking ID for the parking that you have permissions. 2) GET /bookings-available to check availability (and price) for specific dates. 3) POST /bookings to create the booking. 4) PUT /bookings/{id} to modify a booking. 5) DELETE /bookings/{id} to cancel a booking.

Parkings Collection [/parkings]

This collection only accepts GET verb to request the list of authorized parkings for the token and their IDs which you will need in the POST /bookings phase.

List Parkings [GET]

  • OPTIONAL Parameters
    • per_page (integer) - Number of results per page. Default is 20 max is 1000.

Possible HTTP status codes:

Code Description
200 OK
400 Bad request
401 Unauthorized, f.i. No token was provided
403 Forbidden, f.i. This token has no permission at this parking
  • Response 200 (application/json)
{
  "data":
  [
      {
        "id": 1,
        "name": "Example Parking in Madrid",
        "city": "Madrid",
        "country": {
          "code": "ES",
          "name": "Italia"
        },
        "timezone": "Europe/Madrid",
        "currency": "EUR",
        "vat_percentage": 21,
        "required_fields": []
      },
      {
        "id": 2,
        "parkingName": "Example Parking in Rome",
        "city": "Rome",
        "country": {
          "code": "IT",
          "name": "Italia"
        },
        "timezone": "Europe/Rome",
        "currency": "EUR",
        "vat_percentage": 22,
        "required_fields": [
            "vehicle_plate"
        ]
      }
  ]
}

Bookings available Collection [/bookings-available]

Be careful with timezones and DTS changing dates because sometimes a booking from 8AM today to 8AM tomorrow can be a 23, 24 or 25 hours booking and most parkings calculate their rates using 24-hour schemas. The dates and times must be in the timezone of the parking.

The response will be an array including a json object for each requested parking (given that the parking id exists and the channel has permissions to access it). If the places_available attribute is >= 1, then there is availability for a booking.

  • Parameters
    • parking_id (integer) - REQUIRED ID of the Parking. You can pass up to 10 IDs separated by comma.
    • arrival_date (ISO 8601 date YYYY-MM-DD) - REQUIRED Date of arrival (at parking timezone), f.i. 2025-05-28. Parkings normally don't admit bookings more than 365 days in advance. But some parkings can have different policies.
    • arrival_time (ISO 8601 time hh:mm ) - REQUIRED Hour of arrival in 24 hours format (at parking timezone), f.i. 07:00 for 7AM.
    • departure_date (ISO 8601 date YYYY-MM-DD) - REQUIRED Date of departure (at parking timezone), f.i. 2025-05-31. Parkings normally don't admit bookings of more than 31 days.
    • departure_time (ISO 8601 time hh:mm ) - REQUIRED Hour of departure in 24 hours format (at parking timezone), f.i. 18:00 for 6PM.

Check availability [GET]

  • Parkings with dynamic pricing will show something in the price attribute. If price is null you should ignore it and use the rates provided by the parking.
  • The attribute available is a boolean: you MUST NOT try to book in a parking that has "available": false.
  • The attribute available_places can be an integer, displaying the number of available places left for reservations for that period, but can also be null if the parking does not provide this information. This is only informative, and you should not rely on it to make a booking, but it can be interesting to display to your customers in order to rush them to make a reservation if few places are left. available_reason is a string that explains why the parking is not available. If available is true, this field will always be null, but in many cases, even if available is true, this field can be null too: not all unavailable responses have an explanation: if the value is null it means that the parking does not have more available spaces. For instance, if the parking only accepts reservations for 31 days or less, it will show a message here.
  • product_code and product_name are the code and name of the product that the parking is offering. If product_code is null, you can ignore it, but if it is not null, you MUST use this code to make the booking in the POST request. Same with product_name: you MUST ignore if null, but if not, you must send them on the POST request. You can display product_name to the user if you wish. These fields are only used when the parking has dynamic pricing, or it uses a third party integration that we interact with and requires them.

Possible HTTP status codes:

Code Description
200 OK
400 Bad request, f.i. too many parking ids, etc
401 Unauthorized, f.i. No token was provided
403 Forbidden, f.i. This token has no permission at this parking
404 Not found, f.i. Parking not found
  • Response 200 (application/json)
{
    "availability": [
      {
        "parking_id": 1,
        "available": true,
        "available_places": null,
        "available_reason": null,
        "price": null,
        "product_code": null,
        "product_name": null
      },
      {
        "parking_id": 3,
        "available": false,
        "available_places": null,
        "available_reason": "This parking only accepts reservations for 31 days or less.",
        "price": null,
        "product_code": null,
        "product_name": null
      }
    ]
}

In this case, parking #2 has dynamic pricing:

{
    "availability": [
      {
        "parking_id": 2,
        "available": true,
        "available_places": 30,
        "available_reason": null,
        "price": 10.50,
        "product_code": "ONEDAY",
        "product_name": "One day multipass"
      },
      {
        "parking_id": 1,
        "available": true,
        "available_places": null,
        "available_reason": null,
        "price": null,
        "product_code": null,
        "product_name": null
      }
    ]
}

Bookings Collection [/bookings]

List Bookings [GET]

  • OPTIONAL Parameters

    • per_page (integer) - Number of results per page. Default is 20 max is 1000.
    • arrival_date_from (string) - Default: today. Minimum date of arrival (format yyyy-mm-dd) of the bookings to display.
    • arrival_date_to (string) - Default: today. Maximum date of arrival (format yyyy-mm-dd) of the bookings to display.
    • booking_code (string) - Booking code to search. Will compare the exact string.
  • Possible HTTP status codes:

Code Description
200 OK
400 Bad request
401 Unauthorized, f.i. No token was provided
403 Forbidden, f.i. This token has not permissions for this call
  • Response 200 (application/json)
{
    "data": [
        {
            "id": 123456,
            "parking_id": 1,
            "created_at": "2021-06-15T18:46:06Z",
            "arrival_at": "2022-05-30T07:30:00Z",
            "departure_at": "2022-05-30T10:30:00Z",
            "checkin_at": null,
            "checkout_at": null,
            "canceled_at": null,
            "rejected_at": null,
            "prepaid": true,
            "price": 8.9,
            "source": "api",
            "qr_code": null,
            "channel_id": 13,
            "channel_booking_code": "ID12345",
            "channel_margin": 50,
            "product_name": "Basic pass",
            "customer_name": "John Doe",
            "customer_email": null,
            "customer_phone": "+16549879651",
            "customer_language_code": null,
            "customer_country_code": null,
            "room": null,
            "vehicle_type": "car",
            "vehicle_plate": "6655ABC",
            "vehicle_model": "Ford Focus",
            "vehicle_color": "Pink",
            "transportation_passengers": null,
            "transportation_type": null,
            "transportation_departure_time": null,
            "transportation_departure_terminal": null,
            "transportation_departure_number": null,
            "transportation_arrival_time": null,
            "transportation_arrival_terminal": null,
            "transportation_arrival_number": null,
            "special_requests": null,
            "canceled": false,
            "arrival_date": "2022-05-30",
            "arrival_time": "09:30",
            "departure_date": "2022-05-30",
            "departure_time": "12:30"
        }
    ],
    "links": {
        "first": "https://api.parkingsadmin.com/bookings?page=1",
        "last": "https://api.parkingsadmin.com/bookings?page=1",
        "prev": null,
        "next": null
    },
    "meta": {
        "current_page": 1,
        "from": 1,
        "last_page": 1,
        "path": "https://api.parkingsadmin.com/bookings",
        "per_page": 20,
        "to": 15,
        "total": 15
    }
}

Create a New Booking [POST]

Possible HTTP status codes:

Code Description
201 Created (OK!)
400 Bad request, f.i. wrong phone number, missing field, etc
401 Unauthorized, f.i. No token was provided
403 Forbidden, f.i. This token has no permission at this parking
404 Not found, f.i. Parking not found
409 Conflict, f.i. No places available

All strings are 255 characters max unless the contrary is specified. Floats must use dot (.) as decimals separator, not comma (,). Please never use thousands separator.

Although some fields are not required, please provide them if you have them. For instance, if you know the vehicle color or the product name, please provide them. Everything that you would send a parking via e-mail, please send it via API. It helps the parking.

  • REQUIRED Parameters

    • parking_id (integer) - ID of the Parking (check /parkings if you don't know it).
    • booking_code (string, 128) - REQUIRED The channel's booking ID. Can be letters and numbers up to 128 characters. It is STRONGLY recommended being less than 16 characters. Otherwise, the parking will see this field reduced to the last 16 characters in the listings. This field is UNIQUE for the channel. If you send the same booking code twice, the API will return a 409 Conflict error.
    • vehicle_type (string) - REQUIRED possible values: car, motorcycle, van, caravan, bus, truck (other types can be added in the future).
    • price (float) - REQUIRED Total price including taxes. This price is the parking price. It is the one that was charged to the customer and the one that will be used for the settlements. In case that the channel adds Management fees or similar over the price amounts that are not settled with the Parking Operator, these fees SHOULD NOT be included in this price. Maximum 2 decimals.
    • arrival_date (string, ISO 8601 date YYYY-MM-DD) - Date of arrival (at parking timezone), f.i. 2025-05-28.
    • arrival_time (string, ISO 8601 time hh:mm ) - Hour of arrival in 24 hours format (at parking timezone), f.i. 07:00 for 7AM. It is MANDATORY to match this regexp ^(:?[0-1][0-9]|2[0-3]):[0-5][0-9]$.
    • departure_date (string, ISO 8601 date YYYY-MM-DD) - Date of departure (at parking timezone), f.i. 2025-05-31.
    • departure_time (string, ISO 8601 time hh:mm) - Hour of departure in 24 hours format (at parking timezone), f.i. 18:00 for 6PM. It is MANDATORY to match this regexp ^(:?[0-1][0-9]|2[0-3]):[0-5][0-9]$.
    • customer_name (string, 255) - Name and surname of the driver as he/she wants to be addressed. Max. 255 chars.
    • customer_phone (string, 14) - Driver phone. With international prefix and no spaces (f.i. +34999888777, regex: ^\+?[0-9]{1,14}$)
  • MAYBE REQUIRED by some parkings (check /parkings to know the required fields) Max 14 characters.

    • vehicle_plate (string, 255) the license plate of the vehicle, no spaces (f.i. 8744FFH). Max 255 characters.
    • vehicle_model (string, 255) the brand and model of vehicle (f.i. BMW x5). Max 255 characters.
    • vehicle_color (string, 255) the main color of the vehicle, in the parking language or English (f.i. Blue). Max 255 characters.
    • passengers (integer, between 1 and 8) must be a dropdown with options from 1 to 8, numbers.
    • transportation_departure_time and transportation_arrival_time (string, ISO 8601 time hh:mm) must be a hour in this format: 23:59. It is MANDATORY to match this regexp ^(:?[0-1][0-9]|2[0-3]):[0-5][0-9]$. To avoid problems, just show the user a dropdown with all hours in the day with 15 minutes intervals.
    • transportation_departure_terminal and transportation_arrival_terminal (string, 255) is MANDATORY to match with this regexp: ^[a-zA-Z0-9._%\+-]{1,255}$. Max 255 characters.
    • transportation_departure_number and transportation_arrival_number (string, 8) are the flight numbers or vessel names. For flights, it is MANDATORY to match with this regexp: ^[A-Z0-9]{1,8}$.
    • product_code (string, 255) - Code of the product specified by the parking. It is mandatory if the parking uses availability and price and the availability response includes a product_code field. It can also be mandatory if specified by the parking manually.
  • OPTIONAL fields

    • prepaid (boolean) - true if the booking has already been paid, note that most parkings only allow prepaid reservations. OPTIONAL: True by default.
    • margin (float, two decimal positions max) - Margin percentage for the channel. Maximum 2 decimals. OPTIONAL: the default margin defined by the parking will be used if none is set. For instance 10,25% is represented like the decimal 10.25
    • room (string, 10) - For hotels, room of the customer that is booking (max 10 characters).
    • product_name (string, 255) - Name of the product that corresponds to the booking.
    • customer_email (string, 255) - Driver email, please check the syntax before sending. Max 255 characters.
    • customer_language_code (string, 2) - specify the language, use the ISO 639-1 two-letter code that applies in the standard (lowercase). f.i. es for Spanish
    • customer_country_code (string, 2) - Two-letter (uppercase) country code defined in ISO 3166-1. f.i. ES for Spain
    • special_requests (string) Max 255 characters.
  • Minimum Request (application/json)

{
  "parking_id": "2",
  "booking_code": "123456",
  "price": 19.55,
  "arrival_date": "2025-05-29",
  "arrival_time": "10:00",
  "departure_date": "2025-05-30",
  "departure_time": "10:00",
  "customer_name": "John Doe",
  "customer_phone": "+34999888777",
  "vehicle_type": "car"
}
  • Complete Request (application/json)
{
  "parking_id": "2",
  "booking_code": "A23EF61234",
  "margin": 15,
  "price": 19.55,
  "product_name": "1 day multipass",
  "prepaid": true,
  "arrival_date": "2025-05-29",
  "arrival_time": "10:00",
  "departure_date": "2025-05-30",
  "departure_time": "10:00",
  "customer_name": "John Doe",
  "customer_phone": "+34999888777",
  "customer_email": "[email protected]",
  "customer_language_code": "es",
  "customer_country_code": "ES",
  "passengers": 4,
  "vehicle_type": "car",
  "vehicle_plate": "0000EEE",
  "vehicle_model": "Audi Q3",
  "vehicle_color": "White",
  "transportation_departure_time": "13:00",
  "transportation_departure_terminal": "2",
  "transportation_departure_number": "IB8876",
  "transportation_arrival_time": "16:00",
  "transportation_arrival_terminal": "2",
  "transportation_arrival_number": "IB8867",
  "special_requests": "One of the passengers is a RMP."
}
  • Response 201 (application/json)
{
  "id": "6542",
  "parking_id": "2",
  "created_at": "2020-12-28 19:00:00",
  "arrival_at": "2020-12-29 19:00:00",
  "departure_at": "2020-12-23 19:00:00",
  "canceled_at": null,
  "rejected_at": null, 
  "prepaid": true,
  "price": 19.55,
  "source": "email",
  "qr_code": null,
  "channel_id": 13,
  "channel_booking_code": "A23EF61234",
  "channel_margin": 15,
  "product_name": "1 day multipass",
  "customer_name": "John Doe",
  "customer_phone": "+34999888777",
  "customer_email": "[email protected]",
  "customer_language_code": "es",
  "customer_country_code": "ES",
  "room": "404",
  "vehicle_type": "car",
  "vehicle_plate": "0000EEE",
  "vehicle_model": "Audi Q3",
  "vehicle_color": "White",
  "transportation_passengers": 4,
  "transportation_type": null,
  "transportation_departure_time": "13:00:00",
  "transportation_departure_terminal": "2",
  "transportation_departure_number": "IB8876",
  "transportation_arrival_time": "16:00_00",
  "transportation_arrival_terminal": "2",
  "transportation_arrival_number": "IB8867",
  "special_requests": "One of the passengers is a RMP."
}

Booking [/bookings/{id}]

Modify a Booking [PUT]

Works the same way as the POST, please don't include the fields: id, created_at, canceled_at and canceled.

Cancel a Booking [DELETE]

Possible HTTP status codes:

Code Description
200 OK. Will return the object with the canceled_at not null
400 Bad request, f.i. wrong phone number, missing field, etc
401 Unauthorized, f.i. No token was provided
403 Forbidden, f.i. This token has no permission for the booking
404 Not found, f.i. Booking not found
409 Conflict, f.i. Time restricted

No parameters are needed, just DELETE /bookings/{id} and if the time constraint check (some parkings won't allow cancelling when the booking arrival is past), the API will return the booking object with a 200 OK HTTP status code (meaning everything went all right). You will be able to check that canceled_at attribute will be set to the time of the cancellation.

If a 409 Conflict status code is returned it means that the booking is not in a correct status (it might have been already canceled, or it cannot be canceled) or the time limits are not being respected and that booking is not allowed to be canceled. Check the message for details.

If the status is 403 Not allowed means that the client is not correctly authenticated or is trying to cancel a booking that does not belong to him.

The api can return other errors (50x when is a problem in the server). But, as always when the API returns an error it also returns a message field with a message that specifies the details of the problem.

For parameters defined as string, empty strings or strings containing only whitespaces are not accepted. If the parameter is not required, simply omit it in these cases.