Public APIReservations
Resolve Legacy Booking
Convert legacy Strapi v4 booking references to current system
GET
/public/reservations/legacy/{documentId}/resolveOverview
Resolves legacy booking references from Strapi v4 to current external reservation IDs. Used for backward compatibility with old booking systems and member communications that reference legacy document IDs.
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
documentIdpathstringrequiredStrapi v5 document ID (24-character alphanumeric string)
Request
curl -X GET "https://api.journey.com/v1/public/reservations/legacy/abc123def456ghi789jkl012/resolve"Response
externalReservationIdstringrequiredCurrent system external reservation identifier
Response
{
"externalReservationId": "12345678-1234-5678-9012-123456789012"
}Use Cases
Legacy Link Handler
const LegacyBookingHandler = ({ legacyId }) => {
const [externalId, setExternalId] = useState(null);
const [loading, setLoading] = useState(true);
const [error, setError] = useState(null);
useEffect(() => {
const resolveAndRedirect = async () => {
try {
const result = await resolveLegacyBooking(legacyId);
setExternalId(result.externalReservationId);
// Redirect to current reservation URL
window.location.href = `/reservations/${result.externalReservationId}`;
} catch (err) {
setError(err.message);
} finally {
setLoading(false);
}
};
resolveAndRedirect();
}, [legacyId]);
if (loading) {
return (
<div className="legacy-resolver">
<p>Resolving legacy booking reference...</p>
<div className="loading-spinner" />
</div>
);
}
if (error) {
return (
<div className="legacy-error">
<h3>Booking Reference Not Found</h3>
<p>We couldn't find a booking associated with this legacy reference.</p>
<p>Please try using our reservation lookup tool instead.</p>
<a href="/api-reference/public/reservations/lookup-reservation">Look up reservation</a>
</div>
);
}
return null; // Should redirect before rendering
};Email Link Migration
// Handle legacy email links that may contain old document IDs
const handleLegacyEmailLink = async (url) => {
const legacyPattern = /\/legacy\/([a-z0-9]{24})\//i;
const match = url.match(legacyPattern);
if (match) {
const documentId = match[1];
try {
const resolved = await resolveLegacyBooking(documentId);
// Update URL to current format
const newUrl = url.replace(
`/legacy/${documentId}/`,
`/reservations/${resolved.externalReservationId}/`
);
return newUrl;
} catch (error) {
// Fallback to reservation lookup
return '/reservations/lookup';
}
}
return url; // No legacy reference found
};
// Usage in email template or link handler
const EmailLinkHandler = ({ href, children }) => {
const [resolvedHref, setResolvedHref] = useState(href);
useEffect(() => {
const resolveLegacyLink = async () => {
const newHref = await handleLegacyEmailLink(href);
setResolvedHref(newHref);
};
resolveLegacyLink();
}, [href]);
return <a href={resolvedHref}>{children}</a>;
};Batch Legacy Resolution
const resolveLegacyBatch = async (documentIds) => {
const results = await Promise.allSettled(
documentIds.map(async (docId) => {
try {
const resolved = await resolveLegacyBooking(docId);
return {
legacy: docId,
external: resolved.externalReservationId,
success: true
};
} catch (error) {
return {
legacy: docId,
error: error.message,
success: false
};
}
})
);
return results.map(result => result.value);
};
// Usage for migrating member data
const migrateMemberBookings = async (memberBookings) => {
const legacyIds = memberBookings
.filter(booking => booking.type === 'legacy')
.map(booking => booking.documentId);
if (legacyIds.length === 0) return memberBookings;
const resolutions = await resolveLegacyBatch(legacyIds);
return memberBookings.map(booking => {
if (booking.type !== 'legacy') return booking;
const resolution = resolutions.find(r => r.legacy === booking.documentId);
if (resolution?.success) {
return {
...booking,
type: 'current',
externalId: resolution.external,
migrated: true
};
}
return { ...booking, migrationFailed: true };
});
};URL Router Integration
// React Router setup for handling legacy URLs
const LegacyRoute = ({ children }) => {
const { documentId } = useParams();
const navigate = useNavigate();
const [resolving, setResolving] = useState(true);
useEffect(() => {
const resolveAndNavigate = async () => {
try {
const result = await resolveLegacyBooking(documentId);
// Navigate to current route structure
navigate(`/reservations/${result.externalReservationId}`, {
replace: true
});
} catch (error) {
// Navigate to lookup page if resolution fails
navigate('/reservations/lookup', {
replace: true,
state: { error: 'Legacy booking not found' }
});
}
};
resolveAndNavigate();
}, [documentId, navigate]);
return (
<div className="legacy-route-handler">
<p>Updating booking reference...</p>
</div>
);
};
// Router configuration
const AppRouter = () => (
<Routes>
{/* Current routes */}
<Route path="/reservations/:externalId" element={<ReservationPage />} />
{/* Legacy compatibility routes */}
<Route path="/legacy/:documentId/*" element={<LegacyRoute />} />
<Route path="/bookings/legacy/:documentId" element={<LegacyRoute />} />
{/* Fallback */}
<Route path="/reservations/lookup" element={<ReservationLookup />} />
</Routes>
);Document ID Validation
Format Requirements
- Length: Exactly 24 characters
- Characters: Alphanumeric only (a-z, A-Z, 0-9)
- Case: Case insensitive matching
- Pattern:
/^[a-z0-9]{24}$/i
Validation Examples
const validateDocumentId = (documentId) => {
const pattern = /^[a-z0-9]{24}$/i;
if (!documentId) {
return { valid: false, error: 'Document ID is required' };
}
if (documentId.length !== 24) {
return {
valid: false,
error: `Document ID must be 24 characters, got ${documentId.length}`
};
}
if (!pattern.test(documentId)) {
return {
valid: false,
error: 'Document ID must contain only alphanumeric characters'
};
}
return { valid: true };
};
// Valid examples:
// abc123def456ghi789jkl012
// A1B2C3D4E5F6G7H8I9J0K1L2
// 123456789012345678901234
// Invalid examples:
// abc123 (too short)
// abc123def456ghi789jkl012xyz (too long)
// abc123-def456-ghi789-jkl012 (contains hyphens)
// abc123_def456_ghi789_jkl012 (contains underscores)Migration Context
Legacy System Background
- Strapi v4: Previous CMS version with different ID format
- Document IDs: 24-character alphanumeric identifiers
- Migration: Automatic mapping to current external reservation IDs
- Deprecation: Legacy IDs supported but deprecated
Common Migration Scenarios
- Email Links: Old confirmation emails with legacy references
- Member History: Bookings made before system migration
- Deep Links: Saved bookmarks or shared links
- API Integrations: Third-party systems using old references
Transition Timeline
const migrationInfo = {
phases: {
'Pre-2024': 'Strapi v4 document IDs only',
'2024-Q1': 'Migration period - both systems active',
'2024-Q2': 'New external IDs primary, legacy resolution available',
'2024-Q3+': 'Legacy resolution maintained for compatibility'
},
supportPolicy: {
current: 'Full support for legacy resolution',
future: 'Continued support for existing legacy references',
deprecation: 'No new legacy IDs created'
}
};Error Handling
Resolution Failures
const handleResolutionError = (error, documentId) => {
switch (error.status) {
case 404:
return {
message: 'This booking reference is no longer valid.',
action: 'Please use our reservation lookup tool to find your booking.',
fallback: '/reservations/lookup'
};
case 400:
return {
message: 'Invalid booking reference format.',
action: 'Please check the link and try again.',
fallback: '/reservations/lookup'
};
case 429:
return {
message: 'Too many requests. Please wait a moment.',
action: 'Try again in a few minutes.',
retry: true
};
default:
return {
message: 'Unable to resolve booking reference.',
action: 'Please try again or use reservation lookup.',
fallback: '/reservations/lookup'
};
}
};Graceful Degradation
- Automatic fallback to reservation lookup
- Clear error messages for users
- Alternative access methods provided
- Support contact information when needed
Related Endpoints
- Get Reservation Preview - View resolved reservation
- Lookup Reservation - Alternative lookup method