Purpose
Automate AIS US Visa Information appointment scheduling for a supplied locale, credentials, date range, and optional appointment constraints.
When to Use
Use when an AIS NIV applicant must authenticate, handle the site's browser challenge, inspect appointment availability, and submit a booking for a slot in a caller-specified date interval.
Workflow
- Build the locale-specific sign-in URL directly:
https://ais.usvisa-info.com/{locale}/niv/users/sign_in(the observed Canadian locale is{locale}=en-ca). Navigate there withwaitUntil: "domcontentloaded". - Immediately invoke the Browserless challenge solver with
solve({wait:false, timeout:30000}); the AIS page may remain inaccessible until this completes. - Fill the sign-in form using the page's email and password inputs, then submit. Supply credentials only through runtime variables such as
{email}and{password}; never place them in the skill. - If a terms/consent checkbox or acceptance control is shown, accept it before continuing. Locate the appointment-scheduling link or button from the authenticated page rather than guessing an opaque appointment URL, then follow it.
- On the scheduling page, use the page's date and time controls to inspect dates from
{start-date}through{end-date}inclusive. Prefer the site's calendar controls and available-slot elements; do not treat disabled dates or unavailable times as slots. - Select the first slot satisfying the caller's optional constraints, verify that the displayed date/time and applicant details are correct, and submit the booking. If no qualifying slot exists, return an explicit no-availability result instead of submitting.
- After navigation to the confirmation state, run this evaluator on the loaded scheduling or confirmation page to capture the visible appointment data:
(() => {
const text = (el) =>
(el?.innerText || el?.value || el?.getAttribute("aria-label") || "")
.replace(/\\s+/g, " ")
.trim();
const dateNodes = [
...document.querySelectorAll(
'input[id*="appointment_date"], input[name*="appointment_date"], [data-date], td.available, button.available, .ui-datepicker-calendar td',
),
];
const timeNodes = [
...document.querySelectorAll(
'select[id*="appointment_time"], select[name*="appointment_time"], option:not([disabled]), [data-time], .appointment-time, .time-slot',
),
];
const confirmation = [
...document.querySelectorAll(
'h1,h2,h3,.confirmation,.appointment-details,[class*="confirmation"],[class*="appointment"]',
),
]
.map(text)
.filter(Boolean);
return {
url: location.href,
dateControls: dateNodes
.map((el) => ({
text: text(el),
date: el.getAttribute("data-date") || el.value || null,
}))
.filter((x) => x.text || x.date),
timeControls: timeNodes
.map((el) => ({
text: text(el),
time: el.getAttribute("data-time") || el.value || null,
}))
.filter((x) => x.text || x.time),
confirmation,
};
})();Site-Specific Gotchas
- The known entry point is the locale-scoped path
/en-ca/niv/users/sign_in; substitute the caller's locale rather than using the site root. - The run required Browserless
solve(wait:false, timeout:30000)before authenticated interaction; invoke it at the sign-in page. - Appointment and confirmation URLs were not exposed by the observed run, so do not invent an opaque ID-bearing URL. Resolve the scheduling control after login.
- Availability calendars commonly contain disabled dates and multiple row/control types; extract only enabled controls and validate the selected date against both inclusive range boundaries.
- Booking is an external side effect. Confirm the selected date, time, location, and applicant before the final submit action.
Expected Output
Return {status, appointment, url}, where status is booked, no_availability, or error; appointment contains the selected date/time/location when available; and url is the final page URL.