diff --git a/.github/workflows/deployNewHelp.yml b/.github/workflows/deployNewHelp.yml index de8e88902c8c..f113c4f1f3fe 100644 --- a/.github/workflows/deployNewHelp.yml +++ b/.github/workflows/deployNewHelp.yml @@ -50,6 +50,17 @@ jobs: bundler-cache: true working-directory: ./help + # Install Node for _scripts/*.js + - name: Set up Node.js + uses: actions/setup-node@v3 + with: + node-version: '20.15.1' + + # Wil install the _help/package.js + - name: Install Node.js Dependencies + run: npm install + working-directory: ./help # Install the help dependencies, not App + # Manually run Jekyll, bypassing Github Pages - name: Build Jekyll site run: bundle exec jekyll build --source ./ --destination ./_site diff --git a/help/_includes/search.html b/help/_includes/search.html new file mode 100644 index 000000000000..cbdd1ce68a5a --- /dev/null +++ b/help/_includes/search.html @@ -0,0 +1,337 @@ +
+ + + + + +
+ + + + + + + + + + diff --git a/help/_layouts/default.html b/help/_layouts/default.html index cf95e1f54b06..13499e968f3a 100644 --- a/help/_layouts/default.html +++ b/help/_layouts/default.html @@ -4,22 +4,151 @@ {{ page.title }} + + -
- -
- - -
+ + + + + +
{{ content }} + + +
+ +
+

© {{ site.time | date: "%Y" }} Your Company. All rights reserved.

+
- + + + + diff --git a/help/_plugins/51_HeaderIDPostRender.rb b/help/_plugins/51_HeaderIDPostRender.rb index 4af97cc788f6..e2e21b1f486d 100644 --- a/help/_plugins/51_HeaderIDPostRender.rb +++ b/help/_plugins/51_HeaderIDPostRender.rb @@ -1,5 +1,5 @@ require 'nokogiri' -require 'cgi' # Use CGI for URL encoding +require 'cgi' module Jekyll class HeaderIDPostRender @@ -20,32 +20,29 @@ def self.process_page(page) # Parse the page's content for header elements doc = Nokogiri::HTML(page.output) - h1_id = "" - h2_id = "" - h3_id = "" - - # Process all

,

, and

elements - (2..4).each do |level| - doc.css("h#{level}").each do |header| - header_text = header.text.strip.downcase - header_id = CGI.escape(header_text.gsub(/\s+/, '-').gsub(/[^\w\-]/, '')) - - puts " Found h#{level}: '#{header_text}' -> ID: '#{header_id}'" - - # Create hierarchical IDs by appending to the parent header IDs - if level == 2 - h2_id = header_id - header['id'] = h2_id - elsif level == 3 - h3_id = "#{h2_id}:#{header_id}" - header['id'] = h3_id - elsif level == 4 - h4_id = "#{h3_id}:#{header_id}" - header['id'] = h4_id - end - - puts " Assigned ID: #{header['id']}" - end + + # Create an array to store the prefix for each level of header (h2, h3, h4, etc.) + prefix = {} + + # Process all

,

, and

elements in order + doc.css('h2, h3, h4').each do |header| + # Determine the level of the header (h2, h3, or h4) + level = header.name[1].to_i # 'h2' -> 2, 'h3' -> 3, etc. + + # Generate the ID for the current header based on its text + header_text = header.text.strip.downcase + header_id = CGI.escape(header_text.gsub(/\s+/, '-').gsub(/[^\w\-]/, '')) + + # Store the current header's ID in the prefix array + prefix[level] = header_id + + # Construct the full hierarchical ID by concatenating IDs for all levels up to the current level + full_id = (2..level).map { |l| prefix[l] }.join(':') + + # Assign the generated ID to the header element + header['id'] = full_id + + puts " Found h#{level}: '#{header_text}' -> ID: '#{full_id}'" end # Log the final output being written diff --git a/help/_plugins/60_GenerateSearchIndex.rb b/help/_plugins/60_GenerateSearchIndex.rb new file mode 100644 index 000000000000..ac466aa9fc37 --- /dev/null +++ b/help/_plugins/60_GenerateSearchIndex.rb @@ -0,0 +1,41 @@ +require 'open3' + +module Jekyll + class GenerateSearchIndexPostWrite + # Hook into Jekyll's post_write stage, which runs after all files have been written + Jekyll::Hooks.register :site, :post_write do |site| + process_site(site) + end + + def self.process_site(site) + # Define the path to your Node.js script + script_path = File.join(site.source, '_scripts', 'generateSearchIndex.js') + + # Run the Node.js script using Open3.popen3 to stream stdout and stderr + puts "Running Node.js script to generate search index: #{script_path}" + + Open3.popen3("node", script_path) do |stdin, stdout, stderr, wait_thr| + # Stream stdout and stderr to the calling process + stdout_thread = Thread.new do + stdout.each { |line| puts line } + end + + stderr_thread = Thread.new do + stderr.each { |line| warn line } + end + + # Wait for both stdout and stderr threads to finish + stdout_thread.join + stderr_thread.join + + # Check if the process was successful + exit_status = wait_thr.value + unless exit_status.success? + raise "Search index generation failed with exit code #{exit_status.exitstatus}" + end + end + puts "Search index generated successfully" + end + end +end + diff --git a/help/_scripts/generateSearchIndex.js b/help/_scripts/generateSearchIndex.js new file mode 100644 index 000000000000..7294f3654403 --- /dev/null +++ b/help/_scripts/generateSearchIndex.js @@ -0,0 +1,196 @@ +const FlexSearch = require('flexsearch'); +const fs = require('fs'); +const path = require('path'); +const cheerio = require('cheerio'); + +// Function to create the index from HTML files in '_site' directory +async function createIndex() { + console.log("Initializing FlexSearch index..."); + let index; + try { + index = new FlexSearch.Document({ + document: { + id: 'id', + index: ['content'], // Index on the content field + store: ['title', 'url', 'content'], // Store title, URL, and content + } + }); + console.log("FlexSearch index initialized successfully."); + } catch (error) { + console.error("Error initializing FlexSearch index:", error); + process.exit(1); + } + + // Read HTML files from the '_site' directory + const dir = path.join(__dirname, '../_site'); + console.log("Reading HTML files from directory:", dir); + let htmlFiles; + try { + htmlFiles = fs.readdirSync(dir).filter(file => file.endsWith('.html')); + console.log("HTML files found:", htmlFiles); + } catch (error) { + console.error("Error reading directory:", error); + process.exit(1); + } + + // Process each HTML file and add sections to the index + let indexId = 0; + htmlFiles.forEach((file, id) => { + console.log("Processing file:", file); + try { + const content = fs.readFileSync(path.join(dir, file), 'utf8'); + const $ = cheerio.load(content); + + // Extract sections with

,

,

tags and add to index + $('h1, h2, h3').each((i, elem) => { + const title = $(elem).text().trim(); + const sectionContent = $(elem).nextUntil('h1, h2, h3').text().trim(); + const fullContent = `${title} ${sectionContent}`; + + // Generate a URL for the section using the file path and a unique id based on the header + let sectionId; + if (elem.tagName === 'h1' && i === 0) { + // If it's the first

tag, use the root URL (no fragment) + sectionId = ''; + } else { + // Otherwise, generate an ID based on the element's position or existing ID + sectionId = $(elem).attr('id') || `${file}-section-${i}`; + $(elem).attr('id', sectionId); // Ensure the section has an id attribute + } + const sectionUrl = sectionId ? `${file}#${sectionId}` : `${file}#`; + + console.log(`Found section: <${elem.tagName}> ${title} (${fullContent.length} characters)`); + + // Add the section to the index with the section's URL, title, and content + index.add({ + id: indexId++, // Give each a unique numerical index (see FlexSearch "best practices") + title, + url: sectionUrl, + content: fullContent + }); + console.log(`Index entry added for section in file ${file}`); + }); + } catch (error) { + console.error(`Error processing file ${file}:`, error); + process.exit(1); + } + }); + + // Export the index as a JSON file + console.log("Exporting search index as JSON..."); + let exportedIndex = {}; + try { + await index.export((key, data) => { + if (data) { + exportedIndex[key] = data; + } + }); + if (Object.keys(exportedIndex).length === 0) { + console.warn("Warning: Exported index is empty."); + process.exit(1); + } else { + console.log("Index exported successfully."); + + // Write the exported index to a JSON file + const outputPath = path.join(dir, 'searchIndex.json'); + console.log("Writing search index to JSON file:", outputPath); + const outputContent = JSON.stringify(exportedIndex, null, 2); + + fs.writeFileSync(outputPath, outputContent); + console.log("Search index written to JSON file successfully."); + } + } catch (error) { + console.error("Error exporting search index:", error); + process.exit(1); + } +} + +// Function to read and search the index with context and section title +async function searchIndex() { + console.log("\n--- Running Search Test ---"); + const indexPath = path.join(__dirname, '../_site', 'searchIndex.json'); + console.log("Loading search index from file:", indexPath); + let searchIndex; + try { + const indexFile = fs.readFileSync(indexPath, 'utf8'); + searchIndex = JSON.parse(indexFile); + console.log("Search index loaded successfully. Number of entries:", Object.keys(searchIndex).length); + } catch (error) { + console.error("Error loading search index from file:", error); + process.exit(1); + } + + // Initialize a new search index instance with loaded data + let index; + try { + index = new FlexSearch.Document({ + document: { + id: 'id', + index: ['content'], + store: ['title', 'url', 'content'], + } + }); + + // Import each key and corresponding data + for (const [key, data] of Object.entries(searchIndex)) { + await index.import(key, data); + } + + console.log("Search index imported successfully."); + } catch (error) { + console.error("Error initializing or importing search index:", error); + process.exit(1); + } + + // Perform a search test + let testKeyword = "superapp"; + console.log(`Searching for the term '${testKeyword}' in the index...`); + try { + const results = await index.search({ + query: testKeyword, // Do a partial case insensitive search + field: 'content' + }); + if (results && results.length > 0) { + results.forEach(result => { + result.result.forEach(docId => { + const doc = index.store[docId]; + if (doc && doc.content) { // Ensure doc and doc.content are defined + const content = doc.content; + const searchTermIndex = content.toLowerCase().indexOf(testKeyword); + + if (searchTermIndex !== -1) { + // Extract context around the search term (30 characters before and after) + const contextBefore = content.substring(Math.max(0, searchTermIndex - 30), searchTermIndex); + const contextAfter = content.substring(searchTermIndex + testKeyword.length, Math.min(content.length, searchTermIndex + testKeyword.length + 30)); + + // Print the section title, URL, and context + console.log(`Section Title: ${doc.title}`); + console.log(`URL: ${doc.url}`); + console.log(`Context: ...${contextBefore}${testKeyword}${contextAfter}...`); + console.log(); // Add an empty line for better readability + } else { + console.warn(`Warning: Search term '${testKeyword}' not found in document ID: ${docId}`); + } + } else { + console.warn(`Warning: Document content not found for ID: ${docId}`); + } + }); + }); + } else { + console.warn("Warning: No search results found."); + process.exit(1); + } + } catch (error) { + console.error("Error searching the index:", error); + process.exit(1); + } + + process.exit(0); +} + +// Run the main functions +(async () => { + await createIndex(); + await searchIndex(); +})(); + diff --git a/help/package-lock.json b/help/package-lock.json new file mode 100644 index 000000000000..402dfee340e5 --- /dev/null +++ b/help/package-lock.json @@ -0,0 +1,294 @@ +{ + "name": "help", + "version": "1.0.0", + "lockfileVersion": 3, + "requires": true, + "packages": { + "": { + "name": "help", + "version": "1.0.0", + "dependencies": { + "cheerio": "^1.0.0", + "flexsearch": "^0.7.43" + } + }, + "node_modules/boolbase": { + "version": "1.0.0", + "resolved": "https://registry.npmjs.org/boolbase/-/boolbase-1.0.0.tgz", + "integrity": "sha512-JZOSA7Mo9sNGB8+UjSgzdLtokWAky1zbztM3WRLCbZ70/3cTANmQmOdR7y2g+J0e2WXywy1yS468tY+IruqEww==", + "license": "ISC" + }, + "node_modules/cheerio": { + "version": "1.0.0", + "resolved": "https://registry.npmjs.org/cheerio/-/cheerio-1.0.0.tgz", + "integrity": "sha512-quS9HgjQpdaXOvsZz82Oz7uxtXiy6UIsIQcpBj7HRw2M63Skasm9qlDocAM7jNuaxdhpPU7c4kJN+gA5MCu4ww==", + "license": "MIT", + "dependencies": { + "cheerio-select": "^2.1.0", + "dom-serializer": "^2.0.0", + "domhandler": "^5.0.3", + "domutils": "^3.1.0", + "encoding-sniffer": "^0.2.0", + "htmlparser2": "^9.1.0", + "parse5": "^7.1.2", + "parse5-htmlparser2-tree-adapter": "^7.0.0", + "parse5-parser-stream": "^7.1.2", + "undici": "^6.19.5", + "whatwg-mimetype": "^4.0.0" + }, + "engines": { + "node": ">=18.17" + }, + "funding": { + "url": "https://github.com/cheeriojs/cheerio?sponsor=1" + } + }, + "node_modules/cheerio-select": { + "version": "2.1.0", + "resolved": "https://registry.npmjs.org/cheerio-select/-/cheerio-select-2.1.0.tgz", + "integrity": "sha512-9v9kG0LvzrlcungtnJtpGNxY+fzECQKhK4EGJX2vByejiMX84MFNQw4UxPJl3bFbTMw+Dfs37XaIkCwTZfLh4g==", + "license": "BSD-2-Clause", + "dependencies": { + "boolbase": "^1.0.0", + "css-select": "^5.1.0", + "css-what": "^6.1.0", + "domelementtype": "^2.3.0", + "domhandler": "^5.0.3", + "domutils": "^3.0.1" + }, + "funding": { + "url": "https://github.com/sponsors/fb55" + } + }, + "node_modules/css-select": { + "version": "5.1.0", + "resolved": "https://registry.npmjs.org/css-select/-/css-select-5.1.0.tgz", + "integrity": "sha512-nwoRF1rvRRnnCqqY7updORDsuqKzqYJ28+oSMaJMMgOauh3fvwHqMS7EZpIPqK8GL+g9mKxF1vP/ZjSeNjEVHg==", + "license": "BSD-2-Clause", + "dependencies": { + "boolbase": "^1.0.0", + "css-what": "^6.1.0", + "domhandler": "^5.0.2", + "domutils": "^3.0.1", + "nth-check": "^2.0.1" + }, + "funding": { + "url": "https://github.com/sponsors/fb55" + } + }, + "node_modules/css-what": { + "version": "6.1.0", + "resolved": "https://registry.npmjs.org/css-what/-/css-what-6.1.0.tgz", + "integrity": "sha512-HTUrgRJ7r4dsZKU6GjmpfRK1O76h97Z8MfS1G0FozR+oF2kG6Vfe8JE6zwrkbxigziPHinCJ+gCPjA9EaBDtRw==", + "license": "BSD-2-Clause", + "engines": { + "node": ">= 6" + }, + "funding": { + "url": "https://github.com/sponsors/fb55" + } + }, + "node_modules/dom-serializer": { + "version": "2.0.0", + "resolved": "https://registry.npmjs.org/dom-serializer/-/dom-serializer-2.0.0.tgz", + "integrity": "sha512-wIkAryiqt/nV5EQKqQpo3SToSOV9J0DnbJqwK7Wv/Trc92zIAYZ4FlMu+JPFW1DfGFt81ZTCGgDEabffXeLyJg==", + "license": "MIT", + "dependencies": { + "domelementtype": "^2.3.0", + "domhandler": "^5.0.2", + "entities": "^4.2.0" + }, + "funding": { + "url": "https://github.com/cheeriojs/dom-serializer?sponsor=1" + } + }, + "node_modules/domelementtype": { + "version": "2.3.0", + "resolved": "https://registry.npmjs.org/domelementtype/-/domelementtype-2.3.0.tgz", + "integrity": "sha512-OLETBj6w0OsagBwdXnPdN0cnMfF9opN69co+7ZrbfPGrdpPVNBUj02spi6B1N7wChLQiPn4CSH/zJvXw56gmHw==", + "funding": [ + { + "type": "github", + "url": "https://github.com/sponsors/fb55" + } + ], + "license": "BSD-2-Clause" + }, + "node_modules/domhandler": { + "version": "5.0.3", + "resolved": "https://registry.npmjs.org/domhandler/-/domhandler-5.0.3.tgz", + "integrity": "sha512-cgwlv/1iFQiFnU96XXgROh8xTeetsnJiDsTc7TYCLFd9+/WNkIqPTxiM/8pSd8VIrhXGTf1Ny1q1hquVqDJB5w==", + "license": "BSD-2-Clause", + "dependencies": { + "domelementtype": "^2.3.0" + }, + "engines": { + "node": ">= 4" + }, + "funding": { + "url": "https://github.com/fb55/domhandler?sponsor=1" + } + }, + "node_modules/domutils": { + "version": "3.1.0", + "resolved": "https://registry.npmjs.org/domutils/-/domutils-3.1.0.tgz", + "integrity": "sha512-H78uMmQtI2AhgDJjWeQmHwJJ2bLPD3GMmO7Zja/ZZh84wkm+4ut+IUnUdRa8uCGX88DiVx1j6FRe1XfxEgjEZA==", + "license": "BSD-2-Clause", + "dependencies": { + "dom-serializer": "^2.0.0", + "domelementtype": "^2.3.0", + "domhandler": "^5.0.3" + }, + "funding": { + "url": "https://github.com/fb55/domutils?sponsor=1" + } + }, + "node_modules/encoding-sniffer": { + "version": "0.2.0", + "resolved": "https://registry.npmjs.org/encoding-sniffer/-/encoding-sniffer-0.2.0.tgz", + "integrity": "sha512-ju7Wq1kg04I3HtiYIOrUrdfdDvkyO9s5XM8QAj/bN61Yo/Vb4vgJxy5vi4Yxk01gWHbrofpPtpxM8bKger9jhg==", + "license": "MIT", + "dependencies": { + "iconv-lite": "^0.6.3", + "whatwg-encoding": "^3.1.1" + }, + "funding": { + "url": "https://github.com/fb55/encoding-sniffer?sponsor=1" + } + }, + "node_modules/entities": { + "version": "4.5.0", + "resolved": "https://registry.npmjs.org/entities/-/entities-4.5.0.tgz", + "integrity": "sha512-V0hjH4dGPh9Ao5p0MoRY6BVqtwCjhz6vI5LT8AJ55H+4g9/4vbHx1I54fS0XuclLhDHArPQCiMjDxjaL8fPxhw==", + "license": "BSD-2-Clause", + "engines": { + "node": ">=0.12" + }, + "funding": { + "url": "https://github.com/fb55/entities?sponsor=1" + } + }, + "node_modules/flexsearch": { + "version": "0.7.43", + "resolved": "https://registry.npmjs.org/flexsearch/-/flexsearch-0.7.43.tgz", + "integrity": "sha512-c5o/+Um8aqCSOXGcZoqZOm+NqtVwNsvVpWv6lfmSclU954O3wvQKxxK8zj74fPaSJbXpSLTs4PRhh+wnoCXnKg==", + "license": "Apache-2.0" + }, + "node_modules/htmlparser2": { + "version": "9.1.0", + "resolved": "https://registry.npmjs.org/htmlparser2/-/htmlparser2-9.1.0.tgz", + "integrity": "sha512-5zfg6mHUoaer/97TxnGpxmbR7zJtPwIYFMZ/H5ucTlPZhKvtum05yiPK3Mgai3a0DyVxv7qYqoweaEd2nrYQzQ==", + "funding": [ + "https://github.com/fb55/htmlparser2?sponsor=1", + { + "type": "github", + "url": "https://github.com/sponsors/fb55" + } + ], + "license": "MIT", + "dependencies": { + "domelementtype": "^2.3.0", + "domhandler": "^5.0.3", + "domutils": "^3.1.0", + "entities": "^4.5.0" + } + }, + "node_modules/iconv-lite": { + "version": "0.6.3", + "resolved": "https://registry.npmjs.org/iconv-lite/-/iconv-lite-0.6.3.tgz", + "integrity": "sha512-4fCk79wshMdzMp2rH06qWrJE4iolqLhCUH+OiuIgU++RB0+94NlDL81atO7GX55uUKueo0txHNtvEyI6D7WdMw==", + "license": "MIT", + "dependencies": { + "safer-buffer": ">= 2.1.2 < 3.0.0" + }, + "engines": { + "node": ">=0.10.0" + } + }, + "node_modules/nth-check": { + "version": "2.1.1", + "resolved": "https://registry.npmjs.org/nth-check/-/nth-check-2.1.1.tgz", + "integrity": "sha512-lqjrjmaOoAnWfMmBPL+XNnynZh2+swxiX3WUE0s4yEHI6m+AwrK2UZOimIRl3X/4QctVqS8AiZjFqyOGrMXb/w==", + "license": "BSD-2-Clause", + "dependencies": { + "boolbase": "^1.0.0" + }, + "funding": { + "url": "https://github.com/fb55/nth-check?sponsor=1" + } + }, + "node_modules/parse5": { + "version": "7.1.2", + "resolved": "https://registry.npmjs.org/parse5/-/parse5-7.1.2.tgz", + "integrity": "sha512-Czj1WaSVpaoj0wbhMzLmWD69anp2WH7FXMB9n1Sy8/ZFF9jolSQVMu1Ij5WIyGmcBmhk7EOndpO4mIpihVqAXw==", + "license": "MIT", + "dependencies": { + "entities": "^4.4.0" + }, + "funding": { + "url": "https://github.com/inikulin/parse5?sponsor=1" + } + }, + "node_modules/parse5-htmlparser2-tree-adapter": { + "version": "7.0.0", + "resolved": "https://registry.npmjs.org/parse5-htmlparser2-tree-adapter/-/parse5-htmlparser2-tree-adapter-7.0.0.tgz", + "integrity": "sha512-B77tOZrqqfUfnVcOrUvfdLbz4pu4RopLD/4vmu3HUPswwTA8OH0EMW9BlWR2B0RCoiZRAHEUu7IxeP1Pd1UU+g==", + "license": "MIT", + "dependencies": { + "domhandler": "^5.0.2", + "parse5": "^7.0.0" + }, + "funding": { + "url": "https://github.com/inikulin/parse5?sponsor=1" + } + }, + "node_modules/parse5-parser-stream": { + "version": "7.1.2", + "resolved": "https://registry.npmjs.org/parse5-parser-stream/-/parse5-parser-stream-7.1.2.tgz", + "integrity": "sha512-JyeQc9iwFLn5TbvvqACIF/VXG6abODeB3Fwmv/TGdLk2LfbWkaySGY72at4+Ty7EkPZj854u4CrICqNk2qIbow==", + "license": "MIT", + "dependencies": { + "parse5": "^7.0.0" + }, + "funding": { + "url": "https://github.com/inikulin/parse5?sponsor=1" + } + }, + "node_modules/safer-buffer": { + "version": "2.1.2", + "resolved": "https://registry.npmjs.org/safer-buffer/-/safer-buffer-2.1.2.tgz", + "integrity": "sha512-YZo3K82SD7Riyi0E1EQPojLz7kpepnSQI9IyPbHHg1XXXevb5dJI7tpyN2ADxGcQbHG7vcyRHk0cbwqcQriUtg==", + "license": "MIT" + }, + "node_modules/undici": { + "version": "6.19.8", + "resolved": "https://registry.npmjs.org/undici/-/undici-6.19.8.tgz", + "integrity": "sha512-U8uCCl2x9TK3WANvmBavymRzxbfFYG+tAu+fgx3zxQy3qdagQqBLwJVrdyO1TBfUXvfKveMKJZhpvUYoOjM+4g==", + "license": "MIT", + "engines": { + "node": ">=18.17" + } + }, + "node_modules/whatwg-encoding": { + "version": "3.1.1", + "resolved": "https://registry.npmjs.org/whatwg-encoding/-/whatwg-encoding-3.1.1.tgz", + "integrity": "sha512-6qN4hJdMwfYBtE3YBTTHhoeuUrDBPZmbQaxWAqSALV/MeEnR5z1xd8UKud2RAkFoPkmB+hli1TZSnyi84xz1vQ==", + "license": "MIT", + "dependencies": { + "iconv-lite": "0.6.3" + }, + "engines": { + "node": ">=18" + } + }, + "node_modules/whatwg-mimetype": { + "version": "4.0.0", + "resolved": "https://registry.npmjs.org/whatwg-mimetype/-/whatwg-mimetype-4.0.0.tgz", + "integrity": "sha512-QaKxh0eNIi2mE9p2vEdzfagOKHCcj1pJ56EEHGQOVxp8r9/iszLUUV7v89x9O1p/T+NlTM5W7jW6+cz4Fq1YVg==", + "license": "MIT", + "engines": { + "node": ">=18" + } + } + } +} diff --git a/help/package.json b/help/package.json new file mode 100644 index 000000000000..04103940058c --- /dev/null +++ b/help/package.json @@ -0,0 +1,9 @@ +{ + "name": "help", + "version": "1.0.0", + "dependencies": { + "cheerio": "^1.0.0", + "flexsearch": "^0.7.43" + } +} + diff --git a/help/superapp.md b/help/superapp.md index d09860a1ce7e..cd4a12a0d962 100644 --- a/help/superapp.md +++ b/help/superapp.md @@ -2,114 +2,560 @@ layout: product title: Expensify Superapp --- - ## Introduction -The Expensify Superapp packs the full power of 6 world class business, finance, and collaboration products, into a single app that works identically on desktop and mobile, efficiently with your colleagues, and seamlessly with your customers, vendors, family, and friends. +The Expensify Superapp packs the full power of 6 world-class business, finance, and collaboration products into a single app that works identically on desktop and mobile, efficiently with your colleagues, and seamlessly with your customers, vendors, family, and friends. ### When should I use Expensify? -Expensify can do a lot. You should check us out whenever you need to: - -Track and manage expenses -: Whether you are reimbursing employee receipts, deducting personal expenses, or just splitting the bill, Expensify Expense is for you. - -Issue corporate cards -: Skip the reimbursement and capture receipts electronically in realtime by issuing the Expensify Card to yourself and your employees. - -Book and manage travel -: If you are booking your own business trip, arranging a trip for a colleague, or managing the travel of your whole company, Expensify Travel has got you covered. - -Chat with friends and coworkers -: Whether it's collaborating with your team, supporting you client, negotiating with your vendor, or just saying Hi to a friend, Expensify Chat connects you with anyone with an email address or SMS number - -Collect invoice payments online -: Expensify Invoice allows you to collect online payments from consumers and businesses alike – anyone with an email address or SMS number. - -Approve and pay bills online -: Scan, process, and approve bills online using Expensify Billpay, then we'll pay them electronically or via check, whatever they prefer. +Expensify can do a lot. You should check us out whenever you need to: +* **Track and manage expenses** - Whether you are reimbursing employee receipts, deducting personal expenses, or just splitting the bill, Expensify Expense is for you. +* **Issue corporate cards** - Skip the reimbursement and capture receipts electronically in real-time by issuing the Expensify Card to yourself and your employees. +* **Book and manage travel** - If you are booking your own business trip, arranging a trip for a colleague, or managing the travel of your whole company, Expensify Travel has got you covered. +* **Chat with friends and coworkers** - Whether it's collaborating with your team, supporting your client, negotiating with your vendor, or just saying Hi to a friend, Expensify Chat connects you with anyone with an email address or SMS number. +* **Collect invoice payments online** - Expensify Invoice allows you to collect online payments from consumers and businesses alike – anyone with an email address or SMS number. +* **Approve and pay bills online** - Scan, process, and approve bills online using Expensify Billpay, then we'll pay them electronically or via check, whatever they prefer. If you send, receive, or spend money – or even just talk to literally anyone, about literally anything – Expensify is the tool for you. ### Who uses Expensify? -Expensify offers something for everyone. Some people who commonly use us include: - -Individuals -: Millions of individuals use Expensify to track personal expenses to maximize their tax deductions, stay within personal budgets, or just see where their money is going. - -Friends -: Expensify is a great way to split bills with friends, whether it's monthly rent and household expenses, a big ticket bachelorette party, or just grabbing drinks with friends. - -Employees -: Road warriors and desk jockeys alike count on Expensify to reimburse expense reports they create in international airports, swanky hotels, imposing conference centers, quaint coffeeshops, and boring office supply stores around the world. - -Managers -: Bosses manage corporate spend with Expensify to empower their best (and keep tabs on their… not so best), staying ahead of schedule and under budget. - -Accountants -: Internal accountants, fractional CFOs, CAS practices – you name it, they use Expensify to Invoice customers, process vendor bills, capture eReceipts, manage corporate spend: the whole shebang. If you're an accountant, we're already best friends. - -Travel managers -: Anyone looking to manage employee travel has come to the right place. +Expensify offers something for everyone. Some people who commonly use us include: +* **Individuals** - Millions of individuals use Expensify to track personal expenses to maximize their tax deductions, stay within personal budgets, or just see where their money is going. +* **Friends** - Expensify is a great way to split bills with friends, whether it's monthly rent and household expenses, a big-ticket bachelorette party, or just grabbing drinks with friends. +* **Employees** - Road warriors and desk jockeys alike count on Expensify to reimburse expense reports they create in international airports, swanky hotels, imposing conference centers, quaint coffee shops, and boring office supply stores around the world. +* **Managers** - Bosses manage corporate spend with Expensify to empower their best (and keep tabs on their… not-so-best), staying ahead of schedule and under budget. +* **Accountants** - Internal accountants, fractional CFOs, CAS practices – you name it, they use Expensify to Invoice customers, process vendor bills, capture eReceipts, manage corporate spend: the whole shebang. If you're an accountant, we're already best friends. +* **Travel managers** - Anyone looking to manage employee travel has come to the right place. If you are a person online who does basically anything, you can probably do it with Expensify. ### Why should I use Expensify? -Though we do a lot, you've got a lot of options for everything we do. But you should use us because we are: -Simple enough for individuals - We've worked extremely hard to make a product that strips out all the complex jargon and enterprise baggage, and gives you a simple tool that doesn't overwhelm you with functionality and language you don't understand. - -Powerful enough for enterprises -: We've worked extremely hard to make a product that "scales up" to reveal increasingly sophisticated features, but only to those who need it, and only when they need it. Expensify is used by public companies, multinational companies, companies with tens of thousands of employees, non-profits, investment firms, accounting firms, manufacturers, and basically every industry in every currency and in every country around the world. If you are a company, we can support your needs, no matter how big or small. +Though we do a lot, you've got a lot of options for everything we do. But you should use us because we are: +* **Simple enough for individuals** - We've worked extremely hard to make a product that strips out all the complex jargon and enterprise baggage, and gives you a simple tool that doesn't overwhelm you with functionality and language you don't understand. +* **Powerful enough for enterprises** - We've worked extremely hard to make a product that "scales up" to reveal increasingly sophisticated features, but only to those who need it, and only when they need it. Expensify is used by public companies, multinational companies, companies with tens of thousands of employees, non-profits, investment firms, accounting firms, manufacturers, and basically every industry in every currency and in every country around the world. If you are a company, we can support your needs, no matter how big or small. +* **6 products for the price of 1** - Do you pay for an expense management system? A corporate card? A travel management platform? An enterprise chat tool? An invoicing tool? A billpay tool? Now you don't need to. Expensify's superapp design allows us to offer ALL these features on a single platform, at probably less than what you pay for any of them individually. +* **Supports everyone everywhere** - Expensify works on iPhones and Androids, desktops and browsers. We support every currency and can reimburse to almost any country. You don't need to be an IT wizard – if you can type in their email address or SMS number, you can do basically everything with them. +* **You get paid to use it** - Do you spend money? Spend it on the Expensify Card and we pay you up to 2% cashback. It's your money after all. +* **Revenue share for accountants** - Do you manage the books for a bunch of clients? Become an Expensify Approved Accountant and take home 0.5% revenue share. Or share it with your clients as a discount, up to you! -6 products for the price of 1 -: Do you pay for an expense management system? A corporate card? A travel management platform? An enterprise chat tool? An invoicing tool? A billpay tool? Now you don't need to. Expensify's superapp design allows us to offer ALL these features on a single platform, at probably less than what you pay for any of them individually. - -Supports everyone everywhere -: Expensify works on iPhones and Androids, desktops and browsers. We support every currency, and can reimburse to almost any country. You don't need to be an IT wizard – if you can type in their email address or SMS number, you can do basically everything with them. - -You get paid to use it -: Do you spend money? Spend it on the Expensify Card and we pay you up to 2% cashback. It's your money after all. - -Revenue share for accountants -: Do you manage the books for a bunch of clients? Become an Expensify Approved Accountant and take home 0.5% revenue share. Or share it with your clients as a discount, up to you! - -You are in the driver's seat; we're here to earn your business. But we're going to work harder for you than the other guys, and you won't be disappointed. +You are in the driver's seat; we're here to earn your business. But we're going to work harder for you than the other guys, and you won't be disappointed. ## Concepts The Expensify Superapp has a lot of moving pieces, so let's break them down one by one. ### What makes Expensify a superapp? -A "superapp" is a single app that combines multiple products into one seamlessly interconnected experience. Expensify isn't a "suite" of separate products linked through a single account – Expensify is a single app with a single core design that can perform multiple product functions. The secret to making such a seamless experience is that we build all product functions atop the same common core: - -App -: The basis of the superapp experience is the actual app itself, which runs on your mobile phone or desktop computer. (What is the Expensify app?) - -Chats -: Even if you don't plan on using Expensify Chat for enterprise-grade workspace collaboration, chat is infused through the entire product. (What is a chat?) - -Expense -: Even if you aren't actively managing your expenses, you've still got them. Every product that deals with money is ultimately dealing with expenses of some kind. (What is an expense?) - -Workspace -: Though Expensify works great for our millions of individual members, every product really shines when used between groups of members sharing a "workspace". (What is a workspace?) - -Domain -: To support more advanced security features, many products provide extra functionality to members who are on the same email "domain". (What is a domain?) +A "superapp" is a single app that combines multiple products into one seamlessly interconnected experience. Expensify isn't a "suite" of separate products linked through a single account – Expensify is a single app with a single core design that can perform multiple product functions. The secret to making such a seamless experience is that we build all product functions atop the same common core: +* **App** - The basis of the superapp experience is the actual app itself, which runs on your mobile phone or desktop computer. +* **Chats** - Even if you don't plan on using Expensify Chat for enterprise-grade workspace collaboration, chat is infused through the entire product. +* **Expense** - Even if you aren't actively managing your expenses, you've still got them. Every product that deals with money is ultimately dealing with expenses of some kind. +* **Workspace** - Though Expensify works great for our millions of individual members, every product really shines when used between groups of members sharing a "workspace." +* **Domain** - To support more advanced security features, many products provide extra functionality to members who are on the same email "domain." These are the foundational concepts you'll see again and again that underpin the superapp as a whole. ### What is the Expensify app? -Just like your eyes are a window to your soul, the Expensify App is the doorway through which you experience the entire global world of interconnected chat-centric collaborative data that comprises the Expensify network. The main tools of this app consist of: - -Inbox -: The main screen of the app is the Inbox, which highlights exactly what you should do next, consolidated across all products. (What does the Inbox do?) - -Search -: The next major screen is Search, which as you'd expect, let's you search everything across all products, from one convenient and powerful place. (What does Search do?) - -Settings -: Settings wraps up all your personal, workspace, and domain configuration options, all in one helpful space. (What are Expensify's settings?) - -Create -: Finally, the big green plus button is the Create button, which lets you create pretty much anything, across all the products. (What does the Create button do?) - -It's a deceptively simple app, with a few very familiar looking screens and buttons that unlock an incredible range of sophisticated multi-product power. +Just like your eyes are a window to your soul, the Expensify App is the doorway through which you experience the entire global world of interconnected chat-centric collaborative data that comprises the Expensify network. The main tools of this app consist of: +* **Inbox** - The main screen of the app is the Inbox, which highlights exactly what you should do next, consolidated across all products. +* **Search** - The next major screen is Search, which as you'd expect, lets you search everything across all products, from one convenient and powerful place. +* **Settings** - Settings wraps up all your personal, workspace, and domain configuration options, all in one helpful space. +* **Create** - Finally, the big green plus button is the Create button, which lets you create pretty much anything, across all the products. + +It's a deceptively simple app, with a few very familiar-looking screens and buttons that unlock an incredible range of sophisticated multi-product power. + +### Where can I use the Expensify app? +The Expensify app comes in three flavors: +* **Expensify web app** - The Expensify web app is what you would access at new.expensify.com. You can access the web app via a mobile web browser or a desktop web browser – it's optimized to work on both. +* **Expensify mobile app** - The Expensify mobile app works more or less identically to the Expensify web app (when opened in a mobile browser), but is more reliable, higher performance, has better support for notifications. +* **Expensify desktop app** - The Expensify desktop app works more or less identically to the Expensify web app (when opened in a desktop browser), but is more reliable, higher performance, and has better support for notifications. + +Whatever computer or phone you use, Expensify will work on it. + +### What is a workspace? +A workspace groups members together to enable secure sharing and real-time collaboration. Every product adds features to the workspace, but all share the same common baseline: +* **Name** - You can name your workspace anything. Names are not globally unique, but even if every other Alice has their own "Alice's Apples" workspace, yours is definitely the most special. +* **Profile photo** - Give your workspace a great headshot (or logo), or just stick with the beautiful one it is randomly assigned. +* **Description** - Help your members out by giving a good description to your workspace containing copious links and details. +* **Currency** - Though every workspace can support expenses in every currency, for convenience they are all converted into a single currency of your choosing. +* **Headquarters** - Workspaces work great for virtual teams, but some products deal with the physical world and need to know where you are headquartered. +* **Members** - Though there are many situations in which you might want a workspace just for personal use, in general, workspaces work best when they have many members. +* **Admins** - All members have some common elements of access, but "admin" members have enhanced privileges to manage the workspace overall. +* **Rooms** - Every workspace has a series of chat rooms, some of which are built in automatically and some of which are created manually. +* **Plan** - Workspaces come in two flavors, depending on the functionality you need: + * **Collect** - The Collect workspace is optimized for businesses with simpler requirements looking for basic expense management, Expensify Card, invoice collections, and bill pay functionality. + * **Control** - The Control workspace is built for more advanced companies with more powerful needs, such as multi-level approval, advanced domain control, enterprise accounting integrations, and so on. + +Workspaces make up the backbone of Expensify's collaboration features. + +### What is a domain? +A domain is a secondary way of grouping users, generally for more advanced security purposes. Unlike a workspace, which can contain anybody with any email address or SMS number, you join a domain by validating your email address and then optionally "claiming" it as your own. +* **Name** - Each domain corresponds to the "domain name" of your email address (eg, cathy@croissants.com would have the domain of `croissants.com`). Unlike a workspace, you can't rename your domain. +* **Members** - A domain is similar to a workspace in that it represents a group of users. Unlike a workspace, however, domain members are generally limited to those who have validated email contact methods on this domain. +* **Group** - Every member of the domain is a member of exactly one group on the domain. This domain group sets various security rules for that member, such as setting their "preferred workspace." + +Domains allow for more advanced management and top-down control of Expensify members. + +### What does the Inbox do? +Given Expensify's chat-centric design, that makes Expensify in effect a superpowered chat app – and in any chat app, the most important page is the Inbox. The Inbox does a real-time search across all products to highlight exactly what you should do *right now*. A few key features of the Inbox include: +* **Green dot** - Whenever someone is waiting on you to do something – such as an expense you need to reimburse or a booking you need to approve – that thing's chat will be put to the top of the list with a little green dot next to it. +* **Red dot** - Anything you need to finish to accomplish something you started – such as fixing a violation before an expense can be submitted – will also be put to the top of the list with a little red dot next to it. +* **Pinned** - Anything you want to pay special attention to can be manually "pinned" to the top of the Inbox so it stays top of mind. +* **Priority mode** - Though everyone's work style is unique to them, the Expensify app is organized around two modes of prioritization: + * **Most recent mode** - The default mode for new users is to sort the Inbox to put whatever chat was most recently modified at the top. This works particularly well for those engaged in rapid-fire collaboration who want to "go where the action's at." + * **Focus mode** - When the Inbox gets over 30 rows, it automatically switches to "focus mode," which alphabetically organizes the chats and only shows those that are "unread" (ie, have comments you haven't read yet), have a green or red dot, or are pinned. This works well for those engaged in many large group conversations that you might want to monitor, but not necessarily engage with immediately. + +The Inbox is the most powerful page in the app, and where you will spend the bulk of your time. + +### What does Search do? +By and large, pretty much anything. Expensify has a "universal search" design that brings all data objects into a single place, and then lets you search all those objects using an incredibly flexible and powerful search engine. Search consists of the following main pieces: +* **Query** - At the top of the search page is the "query," which formally describes what you are searching for. +* **Datatype selector** - By default, we will search all datatypes simultaneously, but you can narrow the results to a single type. +* **Filters** - Similarly, each datatype has its own properties (eg, an expense has an amount, a trip has a destination), and you can filter on each. +* **Saved searches** - If you dial in a search you intend to do again and again, you can save it for future reuse. + +The Inbox's job is to push information in your direction, but the Search page exists to help you find anything you're looking for. + +### Settings +Every product will generally have its own distinct settings, but all settings are conveniently grouped into three main categories: +* **Account** - Every user has an "account" that stores all data owned by that user. Each individual person has a single user account, though that account can be associated with many contact methods (ie, email addresses and SMS numbers). +* **Workspace** - Group functionality across all products is organized into "workspaces," which allow secure sharing of data and settings between multiple members. +* **Domain** - Many users sign up with an email address, and the end of that address (ie, @company.com) corresponds to the "domain" that user is a member of. Domains are another way to group accounts and securely share data between the domain members. + +Every product adds its own layers of sophistication and power onto the common foundation of this shared superapp core. + +#### What are my account settings? +Your account contains the sum total of all data you own or shared with you, across all products. But all products rely upon the same common set of account properties: +* **Profile** - Your profile allows you to introduce and uniquely identify yourself to everyone else. +* **Wallet** - Your wallet organizes the various financial payment tools (such as the Expensify Card) and bank accounts associated with your account. +* **Preferences** - Your preferences configure high-level settings on how you are notified and how data is presented to you. + +Your personal account contains all the details that make you, you. + +#### What are my profile settings? +Your "profile" is how you identify yourself, both publicly and privately: +* **Your public details** - As the name implies, your public details can be seen by other users. These include: + * **Profile photo** - Your profile photo is the image that is shown next to your name wherever you appear. You can customize this however you please, or a random "avatar" image will be picked for you. + * **Display name** - Your display name is the name that is generally shown next to your photo. If you don't have a display name, then your primary contact method will be shown instead. + * **Contact methods** - Your contact methods are all the email addresses and SMS numbers associated with your account. All contact methods allow you to sign in and associate any email receipts with your account. + * **Primary contact method** - This is the contact method that is highlighted on your profile, and to which all communications are sent. If you are an employee of a business, your primary contact method will typically be your company email address. + * **Secondary contact method** - You can add any number of "secondary" contact methods. These are not shown on your profile, but do allow you to sign into your account. It's helpful to have multiple secondary contact methods (such as a personal email address and personal phone number) to ensure you can access your account if you lose access to your primary contact method (such as your work address). + * **Status** - Your status is an expiring optional icon and message you can set that appears next to your name, such as to hint that you are on vacation or in a meeting, etc. + * **Pronouns** - Your pronouns are an optional tool for allowing you to indicate how you would like to be addressed by others. + * **Timezone** - Your timezone reflects the timezone in which you are currently located. This will generally be set automatically as you travel around the world, but can be manually set as well. + +* **Your private details** - Also as the name implies, your private details are not shown to others but might be required to enable certain functionality: + * **Legal name** - Your legal name is what appears on your government ID, which might differ from how you like to be addressed on a daily basis (ie, your display name). By default, your legal name is assumed to be your display name, but if that is not the case, you can easily correct this. + * **Date of birth** - Your date of birth is the birthday listed on your government ID. + * **Address** - Your address reflects where you would like us to contact you via mail, in the event we ever need to do so (such as to ship you an Expensify Card). + +The combination of your public and private profile gives you the tools to introduce yourself to the world and to us. + +#### What are my wallet settings? +Your wallet is your one-stop shop for all things banking and payment card-related. The major items in your wallet include: +* **Cash** - Just like a regular wallet that has a mix of cash and cards, your Expensify wallet is also able to hold electronic cash you receive from others. +* **Cards** - This contains a central list of every card associated with your Expensify account: + * **Expensify Cards** - Your employer can assign you an Expensify Card that gives you access to company credit for business purchases. + * **Imported cards** - You can import the transactions from your personal or corporate card into Expensify to submit to your company for approval or reimbursement or just to manage for your own needs. + * **Payment** - You can link a credit card to your account for paying your Expensify subscription or to fund your wallet's cash balance. +* **Bank accounts** - This contains a link of bank accounts associated with your Expensify account: + * **Personal bank account** - You can like a personal bank account to either receive company reimbursements or fund your wallet's cash balance. + * **Business bank account** - You can connect your business's bank account to reimburse expenses, issue Expensify Cards, collect online invoice payments, pay bills, and more! + +Just like your normal wallet, a lot can be stuffed into your Expensify wallet, and all of it is priceless. + +#### What are my preferences? +Your preferences are personal settings that affect how we display information to you: +* **Training and marketing** - We, in general, like to occasionally reach out with new information about features, changes, or offers to help – but only if you like. +* **App sounds** - We've worked hard to come up with some subtle audio cues that hint when certain actions happen in the app, but they are entirely optional. +* **Priority mode** - This is how you specify which Inbox priority mode you prefer. +* **Language** - Everybody in the world can use Expensify, and we are supporting an increasing number of languages natively. +* **Theme** - Give into the dark side or stay in the light, we won't judge! +* **Two-factor authentication** - We strongly recommend everyone enable two-factor authentication to secure access to your account. + +Everybody likes things their own way, and preferences are how you make the Expensify app your own. + +#### What are my subscription settings? +Most of Expensify is completely free to use, and millions of members use Expensify without paying anything at all. To unlock our more powerful functionality, create a workspace and pick which products you need – each can be adopted independently, but all are included in the base price (though some products have slightly different nuances: Expensify Card cashback deducts from the bill, Expensify Travel booking fees add to the bill, etc). Regardless of which products you enable, all are billed together via the same subscription. Your subscription consists of the following: +* **Billing card** - Pick a credit or debit card from your wallet to pay your subscription. +* **Subscription length** - Expensify has options for everyone depending on your specific needs, allowing you to balance cost versus commitment: + * **Pay-per-use** - By default, your Expensify account starts with zero-risk, zero-commitment: just use Expensify to your heart's content, and you will be billed for as much or as little as you use the next month. + * **Annual plan** - Once you know how much Expensify you need, lock in a 50% annual plan discount by committing to a certain number of seats for 12 months. The annual plan is configured as follows: + * **Subscription size** - This is the number of seats you commit to purchasing for the next 12 months (billed monthly), at a 50% discounted rate. Any active seats billed at the end of the month in excess of the subscription size are billed at the pay-per-use rate (ie, without the 50% discount). + * **Auto-renew** - Whether to automatically renew this subscription at the end of 12 months, or revert back to pay-per-use (giving up the 50% discount). + * **Auto-increase annual seats** - Whether to automatically increase the number of annual seats you commit to based on the number of seats used. This avoids being accidentally billed for any pay-per-use seats. + +Pick the plan that works for you, and feel free to change as you need. + +#### What is the price of Expensify? +For most users, Expensify is completely free. For business users, the price of Expensify depends on which features are enabled – and with Expensify Card cashback, you can actually be paid to use Expensify! The major variables going into the price of Expensify for your specific needs include the following: +* **Personal use** - Most users enjoy Expensify free of charge, as there is a huge range of free features designed for use by yourself and with your friends. +* **Active seats** - Our paid functionality is largely contained within workspaces and billed on an "active seat" basis. This means at the end of the month, we look over the activity of each workspace member to determine if they used any paid functionality or merely free features: + * **Paid seat** - A workspace member who uses any paid functionality (ie, submitting, approving, or paying expenses) requires a "paid seat." + * **Free seat** - A workspace member who only used free functionality (ie, viewing expenses, chatting outside of an expense report) only requires a "free seat." +* **Paid seat price** - Once we determine how many paid seats you require in a given month, we initially set the price per paid seat at $20/seat/mo for Collect workspaces and $36/seat/mo for Control. +* **Expensify Card discount** - The first modification to the base seat price is to assess how much total spend was approved on the workspace, versus how much of it was spent on the Expensify Card. This will generate a sliding discount ranging from 0% (if you aren't using the card at all) to 50% (if you have used the Expensify Card for at least 50% of your company's spend). The Expensify Card discount is applied to the seat price, which can reduce it down to $10/seat/mo for Collect workspaces or $18/seat/mo for Control. For example: + * If your company spends 0% of the total approved spend on the Expensify Card, you receive no discount. + * If your company spends 25% of the total approved spend on the Expensify Card, you receive a 25% discount off each seat. + * If your company spends 75% of the total approved spend on the Expensify card, you receive a 50% discount off each seat. +* **Annual plan discount** - Next, we determine how many seats you have committed to in your annual plan subscription size and apply an additional 50% discount to those seats – bringing the price down to $5/seat/mo for Collect workspaces, or $9/seat/mo for Control. +* **Expensify Card cashback** - Finally, we calculate how much cashback you earned from spending on the Expensify Card and apply that to the bill, reducing the price further. In many cases, the cashback is larger than the Expensify bill itself, meaning our so-called "paid" features could not only be free, *you can actually be paid to use them.* + +Long story short, depending upon which features you use, you might pay us, it might be free, or we might even pay you. There are a lot of variables involved, so please check out our savings calculator to understand how this will shake out for you. + +#### What the heck is "Save the world"? +Expensify.org’s mission is to empower individuals and communities to eliminate injustice around the world by making giving and volunteering more convenient, meaningful, and collaborative. We simplify full transparency for all, allowing our donors and volunteers to connect and make positive permanent changes. The foundation of Expensify.org was built on applying our expertise in expense management to increase the transparency of how funds are used, the convenience of how donations are gathered, and — most importantly — the human connection between donors, volunteers, and recipients. + +Please note that our funding model is not in the form of a grant given to a nonprofit organization. Instead, we're looking to help amplify the work of individuals who are directly absorbing the costs. + +### What does the big green Create button do? +Saving the best for last is the big green "global create" button. As the name suggests, this allows you to create basically anything your account is allowed to create. The exact options will depend on which products are configured in your workspace, but it can be any of the following: +* **Start chat** - Begins a new chat with one or more users. +* **Track expense** - Tracks an expense for personal use. +* **Submit expense** - Submits an expense to another user for payment. +* **Split expense** - Splits an expense with one or more other users for shared payments. +* **Pay someone** - Sends money to another user from your Expensify wallet balance. +* **Send invoice** - Sends an invoice from a workspace to a customer for online payment. +* **Assign task** - Creates a new task and assigns it to yourself or another user for completion. +* **Book travel** - Books a flight, hotel reservation, or car rental. +* **Quick action** - Repeats the last action you took, most commonly to scan a receipt and submit it via a particular workspace all in a single button. + +As you can see, there's a lot packed into that big button – press it and see what happens! + +## Tutorials +The Expensify superapp has a lot of moving parts, what specifically are you trying to do? Let's point you in the right direction with some step-by-step guides. + +### Role + +#### How do I use Expensify as an individual? +Expensify is designed to be flexible for a wide range of individual use cases. As an individual, you can: +1. Track personal expenses +2. Split bills with friends +3. Collect receipts and categorize them +4. Use Expensify Card for cashback and simplified reimbursement + +Simply log in, navigate to the expense section, and use the Create button to start organizing your expenses. + +#### How do I use Expensify with my friends? +You can use Expensify to settle shared expenses between friends, such as splitting the bill at a restaurant. Here's how: +1. Create an expense and enter the total amount. +2. Choose **Split Expense** and add your friends by entering their email addresses. +3. Expensify will calculate each person's share, and you can easily send a request to them to settle the balance. + +#### How do I use Expensify as an employee? +As an employee, Expensify can help you: +1. Submit expense reports for approval. +2. Use the Expensify Card for company expenses. +3. Book travel and manage expenses during work trips. +4. Communicate with colleagues through integrated chat features. + +After logging in, create an expense report, attach receipts, and submit it for approval through your workspace. + +#### How do I use Expensify as a manager? +Managers can use Expensify to: +1. Approve or reject expense reports from their team. +2. Monitor corporate spending in real-time. +3. Issue Expensify Cards to employees. +4. Set up advanced approval workflows for multi-level reviews. + +Use the Inbox and Workspace features to manage team expenses and approvals efficiently. + +#### How do I use Expensify as an accountant? +Accountants can: +1. Manage multiple clients’ expense workflows through different workspaces. +2. Create invoices and collect payments. +3. Export data directly to accounting software for tax purposes. +4. Benefit from revenue-sharing programs by becoming an Expensify Approved Accountant. + +You can use the Invoice and Bill Pay tools to manage clients' billing, and track expenses for tax reporting. + +#### How do I use Expensify as a travel manager? +Travel managers can: +1. Book and manage employee travel. +2. Track expenses related to flights, hotels, and car rentals. +3. Issue Expensify Cards for travel-related spending. +4. Approve travel expenses before they are reimbursed. + +Simply navigate to the Travel section, where you can manage travel bookings and expense submissions in one place. + +### App + +#### How do I access Expensify on the web? +To visit the Expensify website: +1. Go to www.expensify.com, either on a desktop or mobile browser. + +#### How do I install the Expensify mobile app? +To install the Expensify mobile app: +1. Visit the Expensify for iOS or Expensify for Android app stores. +2. Press **Install**. +3. Follow the prompts to install. +4. Press the Expensify icon in your phone's app list to start. + +#### How do I install the Expensify desktop app? +To install the Expensify desktop app on MacOS: +1. Download the Expensify for MacOS or Expensify for Windows installer. +2. Double-click on the installer to open it. +3. Click the Expensify icon on the taskbar to start. + +#### How do I sign up or sign in to my Expensify account? +Signing up for a new account works the same as signing into an existing account, as follows: +1. Install or access Expensify on any platform: + * Access Expensify on the web. + * Install the Expensify mobile app. + * Install the Expensify desktop app. +2. Choose how you want to connect and press Next: + * Press **Email** and enter your email address, or + * Press **Phone Number** and enter your SMS-compatible phone number. + * Press **Google** and sign in to your Google account, or + * Press **Apple** and sign into your Apple account. +3. If asked to validate your email address, check your email inbox for a magic link and press it. +4. If asked to join, this means that this is the first time you are signing in with this email address or phone number; press **Join** to confirm you entered it correctly. + +#### How do I use a magic link? +Magic links are used for secure login without passwords. When prompted: +1. Check your email for the Expensify Magic Link. +2. Click the link in your email, and it will log you in to Expensify without needing to enter a password. + +#### How do I sign out? +To sign out of Expensify: +1. Press **Settings** in the Expensify app. +2. Scroll to the bottom and press **Sign Out**. + +#### How do I secure my account with two-factor authentication? +To enable two-factor authentication: +1. Press **Settings**. +2. Press **Security**. +3. Press **Two-factor authentication**. +4. Follow the steps to link your mobile phone for 2FA. + +#### How do I close my account? +To close your account: +1. Press **Settings**. +2. Press **Security**. +3. Press **Close account**. +4. Confirm by following the prompts to complete the process. + +### Profile + +#### How do I set my profile photo? +To set your profile photo: +1. Press **Settings**. +2. Press the **pencil icon** next to your existing profile photo. +3. Press **Upload** photo. +4. Follow the prompts on your platform to select your photo from local storage. + +#### How do I change my display name? +To change your display name: +1. Press **Settings**. +2. Press **Profile**. +3. Press **Display name**. +4. Enter your first and last name. +5. Press **Save**. + +#### How do I add a secondary contact method? +To add a secondary contact method: +1. Press **Settings**. +2. Press **Profile**. +3. Scroll to **Contact Methods**. +4. Press **Add Secondary Contact** and enter your additional email or phone number. + +#### How do I change my primary contact method? +To change your primary contact method: +1. Add a new secondary contact method. +2. Press **Make primary** to make it the new primary contact method. + +#### How do I remove a contact method? +To remove a contact method: +1. Press **Settings**. +2. Go to **Profile** and navigate to **Contact Methods**. +3. Select the contact method to remove and press **Remove**. + +#### How do I set my pronouns? +To change your pronouns: +1. Press **Settings**. +2. Press **Profile**. +3. Press **Pronouns**. +4. Start typing your preferred pronouns. +5. Choose your preferred set from the list. + +#### How do I change my timezone? +By default, your timezone will be set automatically to match your system settings. To instead set it manually: +1. Press **Settings**. +2. Press **Profile**. +3. Press **Timezone**. +4. Disable **Automatically determine your location**. +5. Press **Timezone**. +6. Choose your preferred timezone from the list. + +#### How do I set my status? +To set your status: +1. Press **Settings**. +2. Press **Profile**. +3. Press **Status**. +4. Enter your custom status message and choose an emoji (optional). +5. Press **Save**. + +#### How do I change my legal name? +To change your legal name: +1. Press **Settings**. +2. Press **Profile**. +3. Press **Legal Name**. +4. Enter your updated legal name. +5. Press **Save**. + +#### How do I change my date of birth? +To change your date of birth: +1. Press **Settings**. +2. Press **Profile**. +3. Press **Date of Birth**. +4. Update your birth date and press **Save**. + +#### How do I change my address? +To change your address: +1. Press **Settings**. +2. Press **Profile**. +3. Press **Address**. +4. Enter your new address and press **Save**. + +### Workspace + +#### How do I create a workspace? +To create a workspace: +1. Press **Settings**. +2. Press **Workspaces**. +3. Press **Create Workspace**. +4. Follow the steps to name and configure your new workspace. + +#### How do I rename my workspace? +To rename your workspace: +1. Press **Settings**. +2. Press **Workspaces**. +3. Select your workspace and press **Edit**. +4. Change the name and press **Save**. + +#### How do I change the profile photo of my workspace? +To change your workspace's profile photo: +1. Press **Settings**. +2. Press **Workspaces**. +3. Select the workspace and press **Profile Photo**. +4. Upload a new photo and press **Save**. + +#### How do I change the description of my workspace? +To update your workspace description: +1. Press **Settings**. +2. Press **Workspaces**. +3. Select the workspace and press **Description**. +4. Update the text and press **Save**. + +#### How do I change the currency of my workspace? +To change your workspace currency: +1. Press **Settings**. +2. Press **Workspaces**. +3. Select the workspace and press **Currency**. +4. Choose a new default currency and press **Save**. + +#### How do I change the headquarters of my workspace? +To change your workspace's headquarters location: +1. Press **Settings**. +2. Press **Workspaces**. +3. Select the workspace and press **Headquarters**. +4. Update the address and press **Save**. + +#### How do I add or invite someone to my workspace? +To invite a new member: +1. Press **Settings**. +2. Press **Workspaces**. +3. Select the workspace and press **Members**. +4. Press **Add Member** and enter the email addresses of the new members. +5. Press **Invite**. + +#### How do I remove someone from my workspace? +To remove a member from your workspace: +1. Press **Settings**. +2. Press **Workspaces**. +3. Select the workspace and press **Members**. +4. Choose the member to remove and press **Remove Member**. + +#### How do I make someone an admin of my workspace? +To promote a member to an admin: +1. Press **Settings**. +2. Press **Workspaces**. +3. Select the workspace and press **Members**. +4. Select the member and press **Make Admin**. + +#### How do I remove an admin from my workspace? +To remove admin privileges: +1. Press **Settings**. +2. Press **Workspaces**. +3. Select the workspace and press **Members**. +4. Choose the admin and press **Remove Admin**. + +#### How do I enable features on my workspace? +To enable features: +1. Press **Settings**. +2. Press **Workspaces**. +3. Select the workspace and press **Features**. +4. Toggle on the desired features and press **Save**. + +#### How do I upgrade my workspace? +To upgrade a workspace to the Control plan: +1. Press **Settings**. +2. Press **Workspaces**. +3. Select your workspace and press **Upgrade to Control**. +4. Follow the steps to finalize the upgrade. + +#### How do I delete my workspace? +To delete a workspace: +1. Press **Settings**. +2. Press **Workspaces**. +3. Select the workspace and press **Delete Workspace**. +4. Confirm the deletion. + +## FAQ +You've got questions? We've got answers! + +### App + +#### Why don't I set a password? +Expensify uses a "passwordless" design, where each time you sign in, we send a "magic link" to your contact method. This securely authenticates you based on your ability to receive the magic link to the contact method associated with your account. Once signed into a device, you remain signed into that account until you ask to sign out. + +#### Why am I never asked to sign in? +Expensify uses an "infinite sessions" design, where after you sign in on a particular device, you remain signed in indefinitely, until you explicitly sign out. + +#### Why can others message me even if my account is closed? +Like Gmail or iMessage, Expensify is a communications platform designed to let you message anyone with a valid email address or SMS number – whether or not they also use Gmail or iMessage. Accordingly, even if you don't use Expensify (or if you did use it but have since closed your account), other users can still message you using Expensify. + +#### Why can't I block users from messaging me using Expensify? +Similar to how you can't ask Gmail to stop all Gmail users from emailing you, or ask iMessage to stop all iMessage users from texting you, you can't ask Expensify to stop all Expensify users from emailing you. Gmail, iMessage, and Expensify are all tools designed to enable the user to email and SMS other users. + +### Profile + +#### Why do I have both a display name and legal name? +You have a display name to show how you'd like to be publicly identified. Your legal name is used for documentation purposes, such as for billing or tax-related matters, which require your formal identification. + +#### Why do you need my legal name? +Your legal name is necessary for identity verification when issuing payment cards, processing reimbursements, and fulfilling regulatory requirements. + +#### Why do you need my date of birth? +Your date of birth is used for verifying your identity when issuing financial products like the Expensify Card. It helps ensure compliance with regulatory requirements. + +#### Why do you need my home address? +We need your home address for shipping physical items like the Expensify Card and for identity verification when processing reimbursements or payments. + +### Workspace + +#### Why do you need the address of my workspace's headquarters? +We need the headquarters' address to correctly process transactions, apply any local taxes, and ensure compliance with regional laws. + +### Pricing + +#### Which active workspace members require paid seats? +If a workspace member takes any of the following actions inside of a workspace, you will be billed at the end of the month for a paid seat: +* Submit an expense +* Approve an expense +* Pay an expense +* Export an expense +* Chat on an expense report +* and so on + +In general, any action that modifies financial data or participates in a financial workflow is billable activity. + +#### Why do some workspace members using paid features not require paid seats? +In general, any workspace member that uses paid functionality will require a paid seat. However, if you own two or more workspaces with the same member, and the member uses paid functionality on multiple workspaces in a given month (ie, an admin approving expense reports on two different workspaces), you will not be billed twice for the same member – there is no "double dipping."