Portfolio Sample  ·  Technical Writing
BookingBot — Internal Tool Documentation

This documentation was written from scratch by reverse-engineering a live Node.js automation tool with no prior documentation. The source code (~1,500 lines across two files) was read in full to extract every endpoint, parameter, workflow stage, error condition, configuration option, and behavioral edge case — then structured into a user journey from first login through advanced debugging. No documentation brief existed; the content, structure, information architecture, and visual design were all produced independently.

My Role Technical Writer (sole author)
Source Material Raw source code only
Stack Documented Node.js · Playwright · AWS SES
Deliverable Single-page HTML reference doc
Note Company & user details anonymized
bookingbot.internal.example.com / Documentation v1.x
[Company]  ·  Internal Tools

BookingBot

Rerun Booking Automation  —  User Documentation

BookingBot automates rerun sale bookings on the company's e-commerce admin platform. Provide a Sale ID, date, buyer, coordinator, and line sheet — the bot handles the rest.

v1.x · Live AWS · Node.js · Playwright Internal use only
1. Getting Started 2. Using BookingBot 3. API Reference 4. Debug & Monitoring 5. Admin & Config 6. Troubleshooting
Contents
Contents
02 Authentication Magic link · company email only

BookingBot uses a magic link login system. No passwords are stored. Only company email addresses are accepted. Links expire in 15 minutes and sessions last 7 days.

  1. Go to the login page — visit the internal tool URL and navigate to /login
  2. Enter your email — must be your company email address
  3. Check your inbox — click "Log In to BookingBot" (expires in 15 min)
  4. You're in — a signed session cookie is set, valid for 7 days
Login links are single-use. If your link fails, request a new one from the login page. Rate limiting applies per IP address.
03 How It Works

BookingBot runs a headless Chromium browser on the server. When you submit a booking, a background job drives the browser through four stages:

  1. Sale Lookup — navigates to the sale's edit page, finds the supplier link, then searches Revenue Opportunities across up to 8 pages for the matching sale row.
  2. Rerun Click — clicks the Rerun button on the matching RevOpp row and handles confirmation modals automatically.
  3. Form Fill — fills all required fields: date, buyer forecast, buyer, coordinator, facet tags, merchandising tenet, copy notes, MSRP checkbox + URL, price check notes, scheduling, and uploads the line sheet.
  4. Submit & Validate — submits the form and checks the result URL or banner for success. Ship-from address errors are handled automatically with an override attempt.
Session reuse: The browser context persists between bookings using a stored session state file. If the admin session expires mid-run, BookingBot auto-logs in using configured credentials.

04 Booking Form Internal tool web UI

The web UI is the primary way to submit bookings. All fields are required unless noted. After clicking Book Rerun, the page polls for the job result every 3 seconds and displays the outcome automatically.

Duplicate protection: If you submit the same Sale ID + date + buyer + coordinator while a job is already queued or running, BookingBot reuses the existing job rather than creating a duplicate.
05 Field Reference
FieldTypeRequiredDescription
Rerun Sale IDnumberRequiredNumeric Sale ID from the admin platform. Example: 122478
Date to BookdateRequiredRequested start date. Accepts YYYY-MM-DD or M.DD formats. Entered as MM/DD/YYYY in the admin form.
BuyerselectRequiredTeam member who is the buyer. Maps to an internal user ID for the dropdown.
CoordinatorselectRequiredTeam member coordinating the rerun. Can be the same as Buyer.
Line SheetfileRequiredLine sheet attachment (typically .xlsx). Max 10 MB. Attached to the proposal via the admin form's file input.
Debug ModecheckboxOptionalEnables failure-focused debug capture for this run. Requires server-side configuration to activate.
Auto-filled defaults: Buyer Forecast (1000), Merchandising Tenet (Looks awesome), Merch Tenet Notes (NA), Copy Notes (rerun link with sale ID), MSRP URL (company site), Price Check Notes (NA), Scheduling (Flexible), Scheduling Notes (NA), Facet Tags (Other Stuff).
06 Buyers & Coordinators Anonymized for portfolio

The Buyer and Coordinator fields accept either a display name (e.g. alice) or an internal user ID. Names are case-insensitive. The dropdown is searched using the user's internal email address. The table below reflects the structure of the user mapping — specific names and IDs have been anonymized.

User A
ID: xxxxxxxx
Buyer / Coordinator
User B
ID: xxxxxxxx
Buyer / Coordinator
User C
ID: xxxxxxxx
Buyer / Coordinator
User D
ID: xxxxxxxx
Buyer / Coordinator
User E
ID: xxxxxxxx
Buyer / Coordinator
User F
ID: xxxxxxxx
Buyer / Coordinator
User G
ID: xxxxxxxx
Buyer / Coordinator
07 Line Sheet Upload

The line sheet is required for every booking, uploaded as multipart field lineSheet. It does not persist between bookings — you must attach a file each time.

If no line sheet is provided, the booking fails immediately: "Line sheet attachment is required but no file was provided."
Maximum file size is 10 MB. Only 1 file per submission. Filenames are sanitized on upload — special characters are replaced with underscores.
08 Job States

Bookings run asynchronously. The UI polls GET /book-status?id={jobId} every 3 seconds until a terminal state is reached. Jobs are persisted to disk and expire after 24 hours.

Queued
Accepted, waiting for browser
Running
Browser automation in progress
Finished
Proposal created successfully
Failed
Error — see result.message

09 API Reference All endpoints require session auth except /health
POST /book Submit a booking — multipart/form-data
FieldTypeRequiredNotes
saleIdstringRequiredNumeric admin sale ID
dateToBookstringRequiredYYYY-MM-DD format
buyerstringRequiredDisplay name (e.g. "alice") or internal user ID
coordinatorstringRequiredDisplay name or internal user ID
lineSheetfileRequiredLine sheet file attachment (max 10 MB)
buyerForecaststringOptionalDefault: "1000"
merchandisingTenetstringOptionalDefault: "Looks awesome"
debugbooleanOptional"true" to enable debug capture (requires server-side flag)

Response — 202 Accepted

{ "success": true, "message": "Booking queued", "jobId": "a1b2c3d4e5f6", "state": "queued", "debugEnabled": false }
Must use multipart/form-data. JSON body is not accepted. Passing a file path as a field is blocked for security.
GET /book-status?id={jobId}&includeDebug=1 Poll job status

Returns current job state. Poll every 3 seconds until finished or failed. Pass includeDebug=1 to include the full event log.

{ "success": true, "job": { "id": "a1b2c3d4e5f6", "state": "finished", // queued | running | finished | failed "result": { "success": true, "message": "Rerun booked! Sale 122478 → RevOpp 99999 → 04/15/2026", "proposalUrl": "https://admin.example.com/revenue_proposals/99999", "success_reason": "url_match" // url_match | banner_match | ignored_ship_from_validation } } }
GET /health Server health check — no auth required
{ "status": "ok", "version": "1.2.0", "engine": "playwright", "autoLoginConfigured": true }
GET /debug/jobs?limit=20 List recent jobs — auth required

Returns a summary list of recent jobs sorted newest-first. Max limit: 100. Consumed by the Debug Monitor UI at /debug.


10 Debug Monitor UI /debug

Visit /debug after a run to inspect the last 30 jobs. Click any row to view the full JSON payload including selector misses, missing fields, validation errors, and artifact paths.

Screenshots
Checkpoint screenshots at key stages: sale-page, supplier-page, pre-submit, post-submit.
DOM Snapshots
JSON snapshots of all form fields (tag, id, name, required, visible) captured on failure.
Selector Trace
Full log of every selector attempt, success, and miss — grouped by stage and field name.
Event Log
Timestamped event stream per run (up to 400 events). All sensitive data is automatically redacted.
11 Debug Capture Mode

Check "Enable debug capture for this run" in the booking form to activate detailed capture. Requires DEBUG_REMOTE_ENABLED=true on the server.

Debug artifacts are stored in a temporary directory and cleaned up automatically after 24 hours. Max 12 artifacts per job. All paths, emails, and session tokens are redacted in the output.

12 Environment Variables
VariableRequiredDescription
AUTH_SECRETRequiredSecret key for signing session tokens. Must be a strong random value set at deploy time.
ADMIN_EMAILRequiredAdmin platform email for Playwright auto-login when the session expires.
ADMIN_PASSWORDRequiredAdmin platform password for Playwright auto-login.
SES_FROM_EMAILOptionalFrom address used for magic link emails.
APP_URLOptionalPublic URL for magic link generation. Auto-detected from request headers if not set.
PORTOptionalServer port. Default: 3457
CORS_ALLOWED_ORIGINSOptionalComma-separated list of allowed CORS origins.
DEBUG_REMOTE_ENABLEDOptionalSet to "true" to allow debug capture from the UI. Default: false
DEBUG_RETENTION_HOURSOptionalHours to retain debug artifacts. Default: 24
JOB_TTL_HOURSOptionalHours to keep jobs in memory. Default: 24
MAX_UPLOAD_BYTESOptionalMax line sheet size in bytes. Default: 10485760 (10 MB)
CHECKPOINT_SCREENSHOTSOptionalEnable checkpoint screenshots during runs. Also configurable via walkthrough policy file.
APP_VERSIONOptionalVersion string shown in the UI footer and /health endpoint.
WALKTHROUGH_CONFIGOptionalPath to walkthrough policy JSON. Default: config/walkthrough.config.json
13 Session Management

User sessions are stored as HMAC-signed cookies. The browser's admin session is persisted in a local state file and reloaded on every new browser context to avoid re-authentication on each booking.

If the admin platform redirects to the login page mid-booking, BookingBot automatically re-authenticates using the configured credentials, then continues the run from the beginning.
14 Walkthrough Policy config/walkthrough.config.json

Controls which form fields BookingBot is allowed to touch and how to handle validation edge cases.

{ "requiredFieldAllowlist": [ "requested_start_date", "buyer_id", "coordinator_id", "buyer_forecast", "facet_tag_ids", "merchandising_tenet_list", "copy_notes", "msrp_checked", "msrp_url", "price_checked", "price_check_notes", "scheduling", "scheduling_notes", "line_sheet_upload", "submit" ], "ignoredWarnings": [], // regex patterns to suppress from validation errors "checkpointScreenshots": true, "successRules": { "allowUrlMatch": true, "allowBannerMatch": true, "ignoreShipFromValidation": true } }
If requiredFieldAllowlist is non-empty, BookingBot throws an error if it attempts to touch any field not on the list. This is a safety guard against unexpected form changes in the admin platform.

15 Common Errors
Could not find any RevOpp row for sale {saleId}
BookingBot searched up to 8 pages of Revenue Opportunities and couldn't find a matching row. Verify the sale ID is correct and that a RevOpp exists for it in the admin platform. Check /debug for a DOM snapshot to see what was on the page.
Found sale row but could not click its rerun control
The matching row was found but the Rerun button wasn't clickable. The RevOpp may already have a rerun in progress, or the admin UI may have changed. Check the debug selector trace.
Admin session expired. Please re-authenticate.
Occurs in standalone script mode when no auto-login credentials are configured. In API server mode, auto-login handles this automatically. Ensure admin credentials are set in the environment.
Form validation failed: {errors}
The admin platform returned validation errors after submission. Common causes: missing required fields on the admin side, supplier data issues, or a changed form structure. Enable debug mode and check /debug for screenshots and the field inventory.
16 Ship-From Address Validation

The admin platform sometimes rejects proposal submissions with a "ship from address not verified" error. BookingBot handles this automatically in two ways:

  1. Auto-override: BookingBot finds and checks the override checkbox on the form, then resubmits. If successful, the booking completes normally.
  2. Policy ignore: If ignoreShipFromValidation: true is set in the walkthrough config, ship-from-only errors are treated as success and the proposal URL is returned.

If both fail, error code SHIP_FROM_ADDRESS_UNVERIFIED is returned. Verify the supplier's ship-from address in the admin platform and retry.

17 Mock Webhook mock-webhook.js · port 3457

For local development or integration testing, run mock-webhook.js to spin up a lightweight HTTP server that accepts the same POST /book contract without touching the real admin platform.

# Start the mock server node mock-webhook.js # → Mock webhook listening on http://localhost:3457/book # Test with curl curl -X POST http://localhost:3457/book -H "Content-Type: application/json" -d '{"vendor":"Test Vendor","dateToBook":"2026-04-15","saleLink":"https://...","buyer":"Alice","coordinator":"Bob"}'

The mock returns a synthetic revOppId (e.g. MOCK-LX3K2A) and validates required fields, returning 400 if any are missing.

The mock accepts a JSON body. The real API server requires multipart/form-data for the line sheet upload. Adjust your test client accordingly.