Journey Docs
Public APIReservations

Lookup Reservation

Find reservation using confirmation details or reservation ID

POST/public/reservations/lookup

Overview

Allows guests to find their reservation using confirmation code or reservation ID, guest last name, and either check-in or check-out date. Returns the external reservation ID needed to claim the reservation 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

Request Body

At least one of confirmationCode or reservationId is required.

confirmationCodebodystring

Hotel confirmation code (required if reservationId not provided)
Maximum length: 100 characters

reservationIdbodystring

Property management system reservation ID (required if confirmationCode not provided)
Maximum length: 100 characters

checkOutDatebodystring

Check-out date in YYYY-MM-DD format. Required if checkInDate is not provided.

checkInDatebodystring

Check-in date in YYYY-MM-DD format. Required if checkOutDate is not provided.

lastNamebodystringrequired

Guest's last name (case insensitive)
Maximum length: 200 characters

Request

curl -X POST "https://api.journey.com/v1/public/reservations/lookup" \
  -H "Content-Type: application/json" \
  -d '{
    "confirmationCode": "ABC123DEF",
    "checkInDate": "2026-05-28",
    "lastName": "Johnson"
  }'

Response

externalReservationIdstringrequired

External reservation identifier used for claiming reservation and other operations

Response

{
  "externalReservationId": "12345678-1234-5678-9012-123456789012"
}

Use Cases

Reservation Lookup Form

const ReservationLookupForm = ({ onSuccess }) => {
  const [formData, setFormData] = useState({
    confirmationCode: '',
    reservationId: '',
    checkOutDate: '',
    lastName: ''
  });
  const [loading, setLoading] = useState(false);
  const [error, setError] = useState(null);
  
  const handleSubmit = async (e) => {
    e.preventDefault();
    setLoading(true);
    setError(null);
    
    try {
      // Ensure at least one identifier is provided
      if (!formData.confirmationCode && !formData.reservationId) {
        throw new Error('Please provide either a confirmation code or reservation ID');
      }
      
      const result = await lookupReservation({
        confirmationCode: formData.confirmationCode || undefined,
        reservationId: formData.reservationId || undefined,
        checkOutDate: formData.checkOutDate,
        lastName: formData.lastName.trim()
      });
      
      onSuccess(result.externalReservationId);
    } catch (err) {
      setError(err.message);
    } finally {
      setLoading(false);
    }
  };
  
  return (
    <form onSubmit={handleSubmit} className="reservation-lookup">
      <div className="form-group">
        <label>Confirmation Code</label>
        <input
          type="text"
          value={formData.confirmationCode}
          onChange={(e) => setFormData(prev => ({
            ...prev, 
            confirmationCode: e.target.value.toUpperCase()
          }))}
          placeholder="ABC123DEF"
          maxLength={100}
        />
      </div>
      
      <div className="form-divider">OR</div>
      
      <div className="form-group">
        <label>Reservation ID</label>
        <input
          type="text"
          value={formData.reservationId}
          onChange={(e) => setFormData(prev => ({
            ...prev, 
            reservationId: e.target.value
          }))}
          placeholder="RES-2026-001234"
          maxLength={100}
        />
      </div>
      
      <div className="form-group">
        <label>Check-out Date *</label>
        <input
          type="date"
          value={formData.checkOutDate}
          onChange={(e) => setFormData(prev => ({
            ...prev, 
            checkOutDate: e.target.value
          }))}
          required
        />
      </div>
      
      <div className="form-group">
        <label>Last Name *</label>
        <input
          type="text"
          value={formData.lastName}
          onChange={(e) => setFormData(prev => ({
            ...prev, 
            lastName: e.target.value
          }))}
          placeholder="Johnson"
          maxLength={200}
          required
        />
      </div>
      
      {error && (
        <div className="error-message">{error}</div>
      )}
      
      <button type="submit" disabled={loading}>
        {loading ? 'Looking up...' : 'Find Reservation'}
      </button>
    </form>
  );
};

Multi-Step Reservation Flow

const ReservationClaimFlow = () => {
  const [step, setStep] = useState('lookup'); // 'lookup', 'preview', 'claim'
  const [externalId, setExternalId] = useState(null);
  const [reservation, setReservation] = useState(null);
  
  const handleLookupSuccess = async (foundExternalId) => {
    setExternalId(foundExternalId);
    
    // Fetch reservation preview
    try {
      const preview = await getReservationPreview(foundExternalId);
      setReservation(preview);
      setStep('preview');
    } catch (error) {
      console.error('Failed to load reservation preview:', error);
    }
  };
  
  const handleClaimReservation = () => {
    // Redirect to authentication/claim flow
    window.location.href = `/claim/${externalId}`;
  };
  
  return (
    <div className="reservation-flow">
      {step === 'lookup' && (
        <ReservationLookupForm onSuccess={handleLookupSuccess} />
      )}
      
      {step === 'preview' && reservation && (
        <div className="reservation-preview">
          <h2>Reservation Found!</h2>
          <div className="property-info">
            <h3>{reservation.property.name}</h3>
            <p>{reservation.property.address.city}, {reservation.property.address.state}</p>
          </div>
          
          <div className="guest-info">
            <h4>Guest: {reservation.guest.firstName}</h4>
            <p>Points to earn: {reservation.totalAwardedPoints.toLocaleString()}</p>
          </div>
          
          {reservation.isClaimed ? (
            <p>This reservation has already been claimed.</p>
          ) : (
            <button onClick={handleClaimReservation}>
              Claim Reservation & Earn Points
            </button>
          )}
          
          <button onClick={() => setStep('lookup')}>
            Look up different reservation
          </button>
        </div>
      )}
    </div>
  );
};

Form Validation

const validateLookupForm = (formData) => {
  const errors = [];
  
  // Check for at least one identifier
  if (!formData.confirmationCode && !formData.reservationId) {
    errors.push('Please provide either a confirmation code or reservation ID');
  }
  
  // Validate date format
  const dateRegex = /^\d{4}-\d{2}-\d{2}$/;
  if (!dateRegex.test(formData.checkOutDate)) {
    errors.push('Check-out date must be in YYYY-MM-DD format');
  }
  
  // Validate date is not in future (reasonable booking window)
  const checkOutDate = new Date(formData.checkOutDate);
  const maxFutureDate = new Date();
  maxFutureDate.setFullYear(maxFutureDate.getFullYear() + 2);
  
  if (checkOutDate > maxFutureDate) {
    errors.push('Check-out date seems too far in the future');
  }
  
  // Validate last name
  if (!formData.lastName.trim()) {
    errors.push('Last name is required');
  }
  
  if (formData.lastName.length > 200) {
    errors.push('Last name is too long');
  }
  
  return errors;
};

Lookup Strategies

Search Priority

  1. Exact Match: Direct lookup by confirmation code or reservation ID
  2. Fuzzy Matching: Handle common typos in names (implementation dependent)
  3. Case Insensitive: Last name matching ignores case differences

Data Sources

  • Hotel PMS Systems: Primary reservation data source
  • Booking Platforms: OTA and direct booking confirmations
  • Legacy Systems: Historical reservation data

Matching Criteria

  • Confirmation Code/Reservation ID: Exact match required
  • Check-out Date: Must match exactly
  • Last Name: Case-insensitive match
  • Additional Validation: May include room type, dates, etc.

Error Handling

Common Issues

  • Multiple Identifiers: Don't provide both confirmationCode and reservationId
  • Date Format: Use YYYY-MM-DD format only
  • Name Variations: Try exact name as on reservation
  • Recent Bookings: May take time to sync from PMS

Troubleshooting Tips

const troubleshootingMessages = {
  notFound: {
    title: "Reservation not found",
    suggestions: [
      "Double-check your confirmation code or reservation ID",
      "Verify the check-out date is correct",
      "Ensure the last name matches the reservation exactly",
      "Try using the other identifier (confirmation code vs reservation ID)",
      "Recent bookings may take up to 24 hours to sync"
    ]
  },
  validation: {
    title: "Invalid information provided",
    suggestions: [
      "Use YYYY-MM-DD format for dates (e.g., 2026-06-03)",
      "Provide either confirmation code OR reservation ID",
      "Enter the full last name as shown on the reservation"
    ]
  }
};

Security Considerations

Rate Limiting

  • Prevents brute force attempts
  • IP-based tracking across all public endpoints
  • Exponential backoff recommended for retries

Data Privacy

  • Only returns external reservation ID
  • No sensitive guest information exposed
  • Lookup requires multiple verification points

Input Sanitization

  • All inputs validated and sanitized
  • SQL injection prevention
  • XSS protection for all text fields

On this page