A ready trip.
One link to make it yours.
Create an itinerary from our public destination catalogs, then send a link the traveller can open, edit and save. No account or booking is needed to make a plan.
From a request to an editable trip
- Understand the trip. Use the traveller’s destination, dates, adults, rooms and interests. Ask for essential missing details; label any assumptions. One link holds one destination. Supported destinations are Cancún, Ibiza and Dubai.
- Read its current catalog. Choose an exact stay ID and exact stop IDs from the data below. Match budget, access rules and preferences using the available evidence. A catalog is a dated research snapshot; it is not live inventory.
- Make a proposed itinerary. Use local calendar dates and local times. Keep scheduled stops between check-in, inclusive, and checkout, exclusive; leave
day: nullfor ideas to decide later. Set every stop’sconfirmedandgeneratedtofalse. - Encode and return the complete link. Follow the exact packet and UTF-8 encoding below. Present it as “Open your suggested trip” with a short explanation of dates, stay and stops. Preserve the entire
#trip=…fragment; no account, API key or POST request is needed. - The traveller previews it. The destination page opens a trip preview. They choose Open my own copy to add a new trip on their device. Existing trips remain, and the traveller can change dates, replace stops and follow booking links.
A planned trip is not a reservation. Do not describe a stay, table, ticket or event as booked. The traveller verifies availability, prices, admission and any booking terms with the provider. Booking confirmations do not transfer through trip links.
Destinations & public data
Fetch the selected destination’s JSON when preparing the link. These are the same catalogs used by the website. Read destination.id and destination.timezone to confirm the destination.
| Destination | Catalog | ID / local timezone |
|---|---|---|
| Cancún | data.json | cancunAmerica/Cancun |
| Ibiza | ibiza-data.json | ibizaEurope/Madrid |
| Dubai | dubai-data.json | dubaiAsia/Dubai |
stays[] contains hotels and hostels. experiences[] contains places and activities. events.events[] contains dated events, and venues.venues[] holds additional venue detail. Use an entry’s id, not its display name or URL, in the trip. A place can appear in both experiences and venues; deduplicate by ID and read the venue detail too.
For evidence, inspect checked, validUntil, seasonStart, seasonEnd, verifiedForDatesThrough, status, caveat, entryNote, agePolicy and inclusions where available. Follow official, source and sources[].url to verify uncertain or dated details. Missing fields are unknown. A freshness date alone does not establish opening or availability for a future visit.
Place a dated event only between its startDate and endDate, using startTime when documented. Never repeat a one-off event on another date or infer next season’s programme. Observe cancellation, seasonal, guest-only and age restrictions. If evidence does not cover the requested dates, choose another stop or clearly label it as a tentative idea with details to verify.
Exact link format · version 1
https://goaftercheckin.com/{destinationId}.html#trip={base64url}
base64url is UTF-8 JSON encoded as URL-safe base64 without = padding. Replace + with - and / with _. Do not call btoa() directly on Unicode JSON, percent-encode the payload, add query parameters, or append another fragment. The hosted site redirects .html routes to their clean URL; the browser retains the trip fragment.
| Field | Value |
|---|---|
format / version | Exactly "after-check-in-trip" and number 1. |
destinationId | "cancun", "ibiza" or "dubai"; must match the destination page and catalog. |
name | A short, non-sensitive trip title; maximum 60 text units. |
plan.version | Number 1. |
plan.trip | {checkin, checkout, nights, adults, rooms}. Real YYYY-MM-DD dates; 1–90 nights; integer adults 1–8; integer rooms 1–4 and no more than adults. The reader recalculates nights. Use null only when dates are undecided. |
plan.hotelId | An exact stays[].id from this destination, or null for no selected stay. Maximum ID length 100. |
plan.alternates | Array of up to 12 distinct stay IDs, or []. |
plan.pace | "easy", "balanced" or "full". |
plan.note | Optional planning text, maximum 1,500 text units; use "" for none. |
plan.items | Array of up to 200 stops. Each uses the item fields below. The byte limit usually requires a much smaller plan. |
Each stop in plan.items
Set refId to an exact catalog ID and customName to "". For a personal activity such as “Free afternoon”, use refId: null and a customName of up to 100 text units. Do not use a custom name to imply that an unverified venue is in the catalog.
Include day (YYYY-MM-DD or null), time (local 24-hour HH:mm or ""), note (up to 500 text units), confirmed: false and generated: false. generated is reserved for the site’s own suggestion lifecycle. Item uid values are unnecessary; the reader creates new ones.
Text limits use JavaScript UTF-16 string length; some emoji use more than one text unit. The decoded JSON must be at most 12,000 UTF-8 bytes and the full URL at most 16,000 characters. Use short notes and fewer stops to stay below both limits. Invalid stay IDs are cleared and unknown stops without a custom name are dropped; verify every ID before returning the link. The decoder checks the format, but does not verify bookings, opening hours or itinerary feasibility.
A complete working example
Two adults, one room, 5–8 March 2027, with Riu Caribe as a suggested stay, a beach idea at Playa Delfines and a tentative Coco Bongo night. These are real catalog IDs and illustrative dates. No future opening, event programme, price or availability is claimed.
Open the example tripDownload the complete JSON packet and standalone JavaScript encoder. Update the example’s dates and selections for the traveller; do not reuse its dates as a default. The packet is for a link, not the planner’s Load file control.
View the complete example packet
{
"format": "after-check-in-trip",
"version": 1,
"destinationId": "cancun",
"name": "Cancún · beach + nights",
"plan": {
"version": 1,
"trip": {
"checkin": "2027-03-05",
"checkout": "2027-03-08",
"nights": 3,
"adults": 2,
"rooms": 1
},
"hotelId": "riu-caribe",
"alternates": [],
"pace": "balanced",
"note": "Example itinerary only. Check opening dates, admission and availability before booking.",
"items": [
{
"refId": "playa-delfines",
"customName": "",
"day": "2027-03-06",
"time": "",
"note": "A beach idea; check local conditions.",
"confirmed": false,
"generated": false
},
{
"refId": "coco-bongo-cancun",
"customName": "",
"day": "2027-03-07",
"time": "",
"note": "Tentative night out; verify the programme and tickets for this date.",
"confirmed": false,
"generated": false
}
]
}
}Executable JavaScript encoder
This function works in modern browsers and Node.js 18+. Pass the packet above to encodeTripLink(packet). It encodes the supplied packet and checks the byte limit; choose and validate dates and catalog IDs before calling it.
// Standalone encoder for the documented v1 trip-link packet.
// Fetch /trip-link-example.json for a complete example; replace its dates and
// selections using the user's request and the destination's current catalog.
// This encodes a packet; it does not verify bookings, dates or catalog IDs.
export function encodeTripLink(packet) {
if (packet?.format !== 'after-check-in-trip' || packet.version !== 1 || packet.plan?.version !== 1) {
throw new Error('Use the documented version 1 trip packet.');
}
if (!['cancun', 'ibiza', 'dubai'].includes(packet.destinationId)) {
throw new Error('Choose a supported destination.');
}
const bytes = new TextEncoder().encode(JSON.stringify(packet));
if (bytes.length > 12000) throw new Error('Trip packet exceeds 12,000 UTF-8 bytes.');
let binary = '';
for (const byte of bytes) binary += String.fromCharCode(byte);
const encoded = btoa(binary).replace(/\+/g, '-').replace(/\//g, '_').replace(/=+$/, '');
const link = `https://goaftercheckin.com/${packet.destinationId}.html#trip=${encoded}`;
if (link.length > 16000) throw new Error('Trip link exceeds 16,000 characters.');
return link;
}
Browser example, run from an After Check-in page:
const { encodeTripLink } = await import('/trip-link-example.js');
const packet = await (await fetch('/trip-link-example.json')).json();
const link = encodeTripLink(packet);
console.log(link); // Return this complete URL to the traveller.
Verify with the website’s own decoder
The public trip-sharing.js module exports createTripShareUrl and readSharedTrip; it imports trip-store.js. In a browser on this site, or after downloading both modules together for local use, validate the link against the selected catalog:
const { readSharedTrip } = await import('/trip-sharing.js');
const catalog = await (await fetch('/data.json')).json(); // Cancún
const decoded = readSharedTrip(link, packet.destinationId, catalog);
if (!decoded || decoded.droppedItemCount ||
decoded.plan.hotelId !== packet.plan.hotelId ||
decoded.plan.items.length !== packet.plan.items.length ||
!['checkin', 'checkout', 'nights', 'adults', 'rooms'].every(
key => decoded.plan.trip?.[key] === packet.plan.trip?.[key])) {
throw new Error('Review the dates and catalog IDs before sharing.');
}
For Ibiza or Dubai, change the catalog path as listed above. Also compare the decoded stops’ dates, times and IDs with what you intended. A successful decode means the link can be read; it does not establish that the proposed trip can be booked.
For search and agent discovery
Start at llms.txt for a compact index of this guide and the catalogs. The robots policy describes crawling permissions, and the sitemap lists public pages. Trip creation instructions live here so they can be read independently of crawler rules. Publishing these resources makes the workflow discoverable; each search engine and assistant decides whether to index or use them.
Questions or a broken link? Contact After Check-in.