Journey Docs
Public APIReservations

Get Reservation Preview

Get basic reservation information for claiming by guests

GET/public/reservations/{externalId}/preview

Overview

Returns basic reservation information including guest name, property details, and points earned. Used by guests to preview their reservation before claiming it in the Journey loyalty program.

Authentication

No authentication required. This is a public endpoint with rate limiting.

Rate Limiting

  • Limit: 10 requests per minute per IP address
  • Window: 60 seconds
  • Headers: Rate limit information included in response headers

Path Parameters

externalIdpathstringrequired

External reservation ID (UUID format)

Request

curl -X GET "https://api.journey.com/v1/public/reservations/12345678-1234-5678-9012-123456789012/preview"

Response

externalIdstringrequired

External reservation identifier

isClaimedbooleanrequired

Whether the reservation has been claimed by a Journey member

totalAwardedPointsnumberrequired

Total loyalty points that will be awarded for this stay

guestobjectrequired

Guest information

guest object
firstNamestringrequired

Primary guest's first name

propertyobjectrequired

Property information

property object
namestringrequired

Property name

allianceMemberbooleanrequired

Whether the property is part of the Journey alliance

mediaarrayrequired

Property images

media items
urlstringrequired

Full resolution image URL

thumbnailUrlstringrequired

Thumbnail image URL

addressobjectrequired

Property location

address object
citystringrequired

City name

statestringrequired

State or province

latitudenumberrequired

Latitude coordinate

longitudenumberrequired

Longitude coordinate

brandobjectrequired

Brand information

brand object
namestringrequired

Brand name

logostringrequired

Brand logo URL

Response

{
  "externalId": "12345678-1234-5678-9012-123456789012",
  "isClaimed": false,
  "totalAwardedPoints": 2500,
  "guest": {
    "firstName": "Sarah"
  },
  "property": {
    "name": "Journey Beach Resort & Spa",
    "allianceMember": true,
    "media": [
      {
        "url": "https://cdn.journey.com/properties/beach-resort-hero.jpg",
        "thumbnailUrl": "https://cdn.journey.com/properties/beach-resort-hero-thumb.jpg"
      },
      {
        "url": "https://cdn.journey.com/properties/beach-resort-pool.jpg",
        "thumbnailUrl": "https://cdn.journey.com/properties/beach-resort-pool-thumb.jpg"
      }
    ],
    "address": {
      "city": "Cancun",
      "state": "Quintana Roo",
      "latitude": 21.1619,
      "longitude": -86.8515
    },
    "brand": {
      "name": "Journey Resorts",
      "logo": "https://cdn.journey.com/brands/journey-resorts-logo.svg"
    }
  }
}

Use Cases

Reservation Claim Flow

const ReservationClaimPreview = ({ externalId }) => {
  const [reservation, setReservation] = useState(null);
  const [loading, setLoading] = useState(true);
  
  useEffect(() => {
    const fetchReservation = async () => {
      try {
        const data = await getReservationPreview(externalId);
        setReservation(data);
      } catch (error) {
        console.error('Failed to fetch reservation:', error);
      } finally {
        setLoading(false);
      }
    };
    
    fetchReservation();
  }, [externalId]);
  
  if (loading) return <div>Loading reservation...</div>;
  if (!reservation) return <div>Reservation not found</div>;
  
  return (
    <div className="reservation-preview">
      <div className="property-info">
        <img 
          src={reservation.property.media[0]?.thumbnailUrl} 
          alt={reservation.property.name}
        />
        <h2>{reservation.property.name}</h2>
        <p>{reservation.property.address.city}, {reservation.property.address.state}</p>
      </div>
      
      <div className="guest-info">
        <h3>Welcome, {reservation.guest.firstName}!</h3>
        {reservation.isClaimed ? (
          <p>This reservation has already been claimed.</p>
        ) : (
          <div>
            <p>Earn {reservation.totalAwardedPoints.toLocaleString()} points for this stay!</p>
            <button onClick={() => claimReservation(externalId)}>
              Claim Reservation
            </button>
          </div>
        )}
      </div>
    </div>
  );
};

Points Display

const formatPointsEarned = (points) => {
  if (points === 0) return 'No points available';
  if (points < 1000) return `${points} points`;
  if (points < 10000) return `${(points / 1000).toFixed(1)}K points`;
  return `${Math.round(points / 1000)}K points`;
};

const PointsEarnedBadge = ({ points, isClaimed }) => {
  const formattedPoints = formatPointsEarned(points);
  
  return (
    <div className={`points-badge ${isClaimed ? 'claimed' : 'available'}`}>
      <span className="points-amount">{formattedPoints}</span>
      <span className="points-status">
        {isClaimed ? 'Earned' : 'Available to Earn'}
      </span>
    </div>
  );
};

Property Location Display

const PropertyLocation = ({ property }) => {
  const { address, brand } = property;
  
  const openInMaps = () => {
    const url = `https://maps.google.com/?q=${address.latitude},${address.longitude}`;
    window.open(url, '_blank');
  };
  
  return (
    <div className="property-location">
      <div className="brand-info">
        <img src={brand.logo} alt={brand.name} className="brand-logo" />
        <span className="brand-name">{brand.name}</span>
      </div>
      
      <div className="location-info">
        <span className="location">{address.city}, {address.state}</span>
        <button onClick={openInMaps} className="map-link">
          View on Map
        </button>
      </div>
      
      {property.allianceMember && (
        <span className="alliance-badge">Journey Alliance Member</span>
      )}
    </div>
  );
};

Error Handling

Common Error Scenarios

  • Invalid UUID Format: Ensure external ID is valid UUID
  • Reservation Not Found: Handle gracefully with user-friendly message
  • Rate Limiting: Implement retry logic with exponential backoff
  • Network Errors: Show offline/error states

Retry Logic

const fetchWithRetry = async (url, options = {}, maxRetries = 3) => {
  for (let attempt = 1; attempt <= maxRetries; attempt++) {
    try {
      const response = await fetch(url, options);
      
      if (response.status === 429) {
        // Rate limited, wait and retry
        const delay = Math.pow(2, attempt) * 1000; // Exponential backoff
        await new Promise(resolve => setTimeout(resolve, delay));
        continue;
      }
      
      return response;
    } catch (error) {
      if (attempt === maxRetries) throw error;
      
      // Wait before retry
      const delay = Math.pow(2, attempt) * 1000;
      await new Promise(resolve => setTimeout(resolve, delay));
    }
  }
};

Security Considerations

Rate Limiting

  • Implemented to prevent abuse
  • IP-based tracking
  • Headers indicate remaining requests

Data Privacy

  • Only returns basic guest first name
  • No sensitive personal information exposed
  • External ID acts as secure token

Input Validation

  • External ID must be valid UUID format
  • Malformed requests return 400 error

On this page