Availability feed

message AvailabilityFeed {
  FeedMetadata metadata = 1;
  repeated ServiceAvailability service_availability = 2;
}
message ServiceAvailability {
  // If provided, we will consider the Availability entities provided to be a
  // complete snapshot from [start_timestamp_restrict, end_timestamp_restrict).
  // That is, all existing availability will be deleted if the following
  // condition holds true:
  //
  //    start_timestamp_restrict <= Availability.start_sec &&
  //    Availability.start_sec < end_timestamp_restrict
  //
  // If a duration message is set, the condition is further restricted:
  //   Availability.duration == duration_restrict_sec
  //
  // If a resource_restrict message is set, the condition is further restricted:
  //
  //    Availability.resource.staff_id == resource_restrict.staff_id &&
  //    Availability.resource.room_id == resource_restrict.room_id
  //
  // These fields are typically used to provide a complete update of
  // availability in a given time range.
  //
  // Setting start_timestamp_restrict while leaving end_timestamp_restrict unset
  // is interpreted to mean all time beginning at start_timestamp_restrict.
  //
  // Setting end_timestamp_restrict while leaving start_timestamp_restrict unset
  // is interpreted to mean all time up to the end_timestamp_restrict.
  //
  // In Unix time format (seconds since the epoch) from UTC. (both optional)
  int64 start_timestamp_restrict = 1;
  int64 end_timestamp_restrict = 2;

  // If provided, the timestamp restricts will be applied only to the given
  // merchant or service.
  //
  // These fields are typically used to provide complete snapshot of
  // availability in a given range (defined above) for a specific merchant or
  // service.
  //
  // Leaving these fields unset, or setting these to the empty string or null,
  // is interpreted to mean that no restrict is intended. (both optional)
  string merchant_id_restrict = 3;
  string service_id_restrict = 4;

  // Setting duration further restricts the scope of the update to just the
  // availability with matching duration.
  //
  // In seconds. (optional)
  int64 duration_restrict_sec = 7;

  // Setting resources_restrict further restricts the scope of the update to
  // just this set of resources. All id fields of the resources must match
  // exactly. (optional)
  Resources resources_restrict = 6;

  // All Availability Slots included in this Service Availability (required)
  repeated Availability availability = 5;
}
// An availability of the merchant's service, indicating time and number
// of spots.
// The availability feed should be a list of this message.
// Please note that it's up to the partner to call out all the possible
// availabilities.
// If a massage therapist is available 9am-12pm, and they provide
// one-hour massage sessions, the aggregator should provide the feed as
//   availability {start_sec: 9am, duration: 60 minutes, ...}
//   availability {start_sec: 10am, duration: 60 minutes, ...}
//   availability {start_sec: 11am, duration: 60 minutes, ...}
// instead of
//   availability {start_sec: 9am, duration: 180 minutes, ...}
//
message Availability {
  // An opaque string from an aggregator to identify a merchant. (required)
  string merchant_id = 1;
  // An opaque string from aggregator to identify a service of the
  // merchant. (required)
  string service_id = 2;
  // Start time of this availability, using epoch time in seconds in UTC.
  //(required)
  int64 start_sec = 3;
  // Duration of the service in seconds, e.g. 30 minutes for a chair massage.
  // (required)
  int64 duration_sec = 4;
  // Number of total spots and open spots of this availability.
  // E.g. a Yoga class of 10 spots with 3 booked.
  //   availability {spots_total: 10, spots_open: 7 ...}
  // E.g. a chair massage session which was already booked.
  //   availability {spots_total: 1, spots_open: 0 ...}
  //
  // Note: If sending requests using the availability compression format defined
  //       below, these two fields will be inferred. A Recurrence
  //       implies spots_total=1 and spots_open=1. A ScheduleException implies
  //       spots_total=1 and spots_open=0.
  // (both required if recurrence not set)
  int64 spots_total = 5;
  int64 spots_open = 6;
  // An optional opaque string to identify this availability slot. If set, it
  // will be included in the requests that book/update/cancel appointments.
  // (optional)
  string availability_tag = 7;

  // Optional resources used to disambiguate this availability slot from
  // others when different staff, room, or party_size values are part
  // of the service.
  //
  // E.g. the same Yoga class with two 2 instructors.
  //  availability { resources { staff_id: "1" staff_name: "Amy" }
  //                 spots_total: 10 spots_open: 7 }
  //  availability { resources { staff_id: "2" staff_name: "John" }
  //                 spots_total: 5 spots_open: 2 }
  // (optional)
  Resources resources = 8;

  // A list of IDs referencing the payment options which can be used to pay
  // for this slot. The actual payment options are defined at the Merchant
  // level, and can also be shared among multiple Merchants.
  //
  // This field overrides any payment_option_ids specified in the service
  // message. Similarly payment_option_ids specified here do NOT have to be
  // present in the service message, though must be defined at the
  // Merchant level.
  // Our current implementation limits the number of entries in this array to
  // one element. Multiple payment_option_id are still allowed at the Service
  // level, but an override at the availability slot level, is limited to a
  // single payment_option_id. (optional)
  repeated string payment_option_id = 9;

  // Recurrence messages are optional, but allow for a more compact
  // representation of consistently repeating availability slots. They typically
  // represent a day's working schedule.
  // ScheduleException messages are then used to represent booked/unavailable
  // time ranges within the work day.
  //
  // Requirements:
  //   1. The expansion of availability slots or recurrences must NOT create
  //      identical slots. If the ids, start_sec, duration_sec, and resources
  //      match, slots are considered identical.
  //   2. Do NOT mix the standard availability format and recurrence within the
  //      slots of a single service. Recurrence benefits merchants/services that
  //      offer appointments. The standard format is geared towards
  //      merchants/services with regularly scheduled classes.
  message Recurrence {
    // The inclusive maximum UTC timestamp the availability repeats until.
    // (required)
    int64 repeat_until_sec = 1;
    // Defines the time between successive availability slots.
    //
    // Example: An availability with a duration of 20 min, a repeat_every_sec of
    // 30 min, a start_sec of 9:00am, and a repeat_until_sec of 11:00am will
    // yield slots at 9-9:20am, 9:30-9:50am, 10-10:20am, 10:30-10:50am,
    // 11-11:20am. (required)
    int32 repeat_every_sec = 2;
  }
  // The recurrence information for the availability, representing more than one
  // start time. A recurrence should contain appointments for one working day.
  // (optional)
  Recurrence recurrence = 10;

  // ScheduleException messages represent booked/unavailable time ranges within
  // the workday, which are exceptions to the recurrence described above. As
  // time slots are booked, the list of exceptions should be updated to reflect
  // the newly unavailable time ranges. The recurrence itself shouldn't be
  // modified.
  message ScheduleException {
    // The time range of the exception. Any slots described by the recurrence
    // which overlap this closed-open time range will be considered unavailable.
    //
    // Example: If the recurrence specifies a duration of 20 min, a
    // repeat_every_sec of 30 min, a start_time of 9:00am, and a
    // repeat_until_sec of 11:00am, then a ScheduleException with a time_range
    // of 9:45am-11:00am would make unavailable the slots at 9:30-9:50am,
    // 10-10:20am, and 10:30-10:50am.
    //
    // Note that because the time range is closed-open, the slot beginning at
    // 11am slot would not be impacted.
    TimeRange time_range = 1;
  }
  // Times when this service cannot be scheduled. To limit the number of
  // schedule_exception messages, consider joining adjacent exceptions.
  // (optional)
  repeated ScheduleException schedule_exception = 11;

  // Defines how a deposit may be charged to the user. Overrides the service
  // deposit if one was specified. Setting this to an empty Deposit message
  // removes any service-level deposit. (optional)
  Deposit deposit = 12;

  // Defines a no show fee that may be charged to the user. Overrides the
  // service no show fee if one was specified. Setting this to an empty
  // NoShowFee message removes any service-level no show fee. (optional)
  NoShowFee no_show_fee = 13;

  // Indicates whether the user must provide a credit card in order to book this
  // availability slot.
  // If the value is not set, it is inherited from the service level if it's set
  // there. (optional)
  RequireCreditCard require_credit_card = 14;

  // Indicates a list of supported ticket types for this availability slot. If
  // unset, all ticket types in the parent service are available for this slot.
  // Note that the values of this field must be defined in the parent service.
  // Examples:
  //
  // * Service with four ticket types:
  // TicketType {ticket_type_id: "adult_1" short_description: "Adult weekdays"}
  // TicketType {ticket_type_id: "adult_2" short_description: "Adult weekends"}
  // TicketType {ticket_type_id: "youth_1" short_description: "Youth weekdays"}
  // TicketType {ticket_type_id: "youth_2" short_description: "Youth weekends"}
  //
  // To represent the inventory during the weekdays:
  //   `availability {ticket_type_id: "adult_1" ticket_type_id: "youth_1"...}`.
  // To represent the inventory during the holidays:
  //   `availability {ticket_type_id: "adult_2" ticket_type_id: "youth_2"...}`.
  //
  // * Service with three ticket types:
  // TicketType {ticket_type_id: "adult" short_description: "Adult"}
  // TicketType {ticket_type_id: "youth" short_description: "Youth"}
  // TicketType {ticket_type_id: "senior" short_description: "Senior"}
  //
  // To indicate that all three ticket types are available for this time
  // slot, use either
  //   `availability {ticket_type_id: "adult" ticket_type_id: "youth"
  //   ticket_type_id: "senior" ...}`
  // or
  //   `availability {...}' (do not set ticket_type_id in this slot).
  //
  //  (optional)
  repeated string ticket_type_id = 15;

  // Availability level scheduling rules.
  message SchedulingRuleOverrides {
    // The last time (in seconds) that this slot is able to be booked. This
    // timestamp must be before the start_sec of the slot to be respected
    // (if users should be able to book after the start time, use service level
    // SchedulingRules.min_booking_before_end_time). If present, will override
    // anything specified in the min_booking_buffer of the corresponding
    // Service's SchedulingRules.
    int64 last_bookable_sec = 1;
  }

  // Availability scheduling rules. If fields are populated, they will override
  // any corresponding scheduling rules on the service-level SchedulingRules.
  SchedulingRuleOverrides scheduling_rule_overrides = 16;
}
// A resource is used to disambiguate availability slots from one another when
// different staff, room or party_size values are part of the service.
// Multiple slots for the same service and time interval can co-exist when they
// have different resources.
message Resources {
  // One of staff_id, room_id, or party_size must be set.

  // Optional ID for a staff member providing the service. This field identifies
  // the staff member across all merchants, services, and availability records.
  // It also needs to be stable over time to allow correlation with past
  // bookings. (optional but required if staff_name is present)
  string staff_id = 1;

  // Optional name of a staff member providing the service. This field will be
  // displayed to users making a booking, and should be human-readable, as
  // opposed to an opaque identifier. (optional but required if staff_id is
  // present)
  string staff_name = 2;

  // An optional ID for the room the service is located in. This field
  // identifies the room across all merchants, services, and availability
  // records. It also needs to be stable over time to allow correlation with
  // past bookings. (optional but required if room_name is present)
  string room_id = 3;

  // An optional name for the room the service is located in. This
  // field will be displayed to users making a booking, and should be human
  // readable, as opposed to an opaque identifier. (optional but required if
  // room_id is present)
  string room_name = 4;

  // Applicable only for Dining: The party size that can be accommodated
  // during this time slot. A restaurant can be associated with multiple Slots
  // for the same time, each specifying a different party_size, if for instance
  // 2, 3, or 4 people can be seated with a reservation. (optional)
  int32 party_size = 5;
}
// A closed-open time range, i.e. [begin_sec, end_sec)
message TimeRange {
  // Seconds of UTC time since Unix epoch (required)
  int64 begin_sec = 1;
  // Seconds of UTC time since Unix epoch (required)
  int64 end_sec = 2;
}
// The price of a service or a fee.
message Price {
  // The price in micro-units of the currency.
  // For example: 1.95 USD is 1950000 in micro-units.
  // If your price contains fractions of the smallest currency unit, then it
  // will be rounded using nearest even rounding (e.g. 2.5 cents rounded
  // to 2 cents, 3.5 cents rounded to 4 cents, 0.5 cents rounded to 0 cents,
  // 2.51 cents rounded to 3 cents). (required)
  int64 price_micros = 1;
  // The currency of the price that is defined in ISO 4217. (required)
  string currency_code = 2;
  // An optional and opaque string that identifies the pricing option that is
  // associated with the extended price. (optional)
  string pricing_option_tag = 3;
}
// A deposit that the user may be charged or have a hold on their credit card
// for.
message Deposit {
  // Deposit amount.
  Price deposit = 1;

  // Minimum advance cancellation for the deposit.
  int64 min_advance_cancellation_sec = 2;

  // Defines how the deposit is determined from the availability.
  PriceType deposit_type = 3;
}
// A fee that a user may be charged if they have made a booking but do not
// show up.
message NoShowFee {
  // The amount the user may be charged if they do not show up for their
  // reservation.
  Price fee = 1;

  // Defines how the fee is determined from the availability.
  PriceType fee_type = 3;
}
// Defines how a total price is determined from an availability.
enum PriceType {
  // The price is for a fixed amount. This is the default value if the field is
  // not set.
  FIXED_RATE_DEFAULT = 0;
  // The price specified is per person, and the total price is calculated
  // according to the party size specified in Resources as
  // price_micros * party_size. A PER_PERSON price must be accompanied by a
  // party size in the availability resources. If it is not, a party size of one
  // is used.
  PER_PERSON = 1;
}
// Defines whether a credit card is required in order to book an appointment.
enum RequireCreditCard {
  // The credit card requirement is not explicitly specified and the
  // behaviour is identical to the one specified for CONDITIONAL.
  REQUIRE_CREDIT_CARD_UNSPECIFIED = 0;

  // Google will require a credit card for the booking if any of the following
  // conditions are met:
  // * the availability has a price and the prepayment_type is REQUIRED
  // * the no_show_fee is set
  // * the deposit field is set.
  REQUIRE_CREDIT_CARD_CONDITIONAL = 1;

  // A credit card is always required in order to book this availability
  // regardless of other field values.
  REQUIRE_CREDIT_CARD_ALWAYS = 2;
}