PageSourceSearch

https://www.echoo.ai/docs

html echoo.ai collected 2026-10-05 13:09:13 UTC 54,032 bytes, 90 lines download raw bytes

1<!DOCTYPE html><html lang="en" class="dark a11y-reduced-motion"><head>
2    <meta charset="UTF-8">
3    <meta name="viewport" content="width=device-width, initial-scale=1.0">
4    <!-- The shell's own title and description. Helmet replaces both on every route,
5         and prerender.js drops this description once Helmet's has rendered - but this
6         is what a route the prerender missed is served, so it mirrors the homepage. -->
7    <title>Echoo API Docs, OpenAPI Spec &amp; CLI for Developers</title>
8    
9
10    <meta name="author" content="Echoo">
11
12    <!-- Theme & Icons -->
13    <meta id="theme-color" name="theme-color" content="#08090B">
14    <meta name="msapplication-TileColor" content="#08090B">
15    <meta name="msapplication-TileImage" content="/mstile-150x150.png">
16    
17    <!-- Favicons - All browsers & platforms -->
18    <link rel="icon" type="image/svg+xml" href="/favicon.svg?v=4">
19    <link rel="icon" type="image/x-icon" href="/favicon.ico">
20    <link rel="icon" type="image/png" sizes="16x16" href="/favicon-16x16.png">
21    <link rel="icon" type="image/png" sizes="32x32" href="/favicon-32x32.png">
22    <link rel="icon" type="image/png" sizes="48x48" href="/favicon-48x48.png">
23    <link rel="apple-touch-icon" sizes="180x180" href="/apple-touch-icon.png">
24    <link rel="manifest" href="/site.webmanifest">
25
26    <!-- Additional SEO -->
27    <meta name="application-name" content="Echoo">
28    <meta name="apple-mobile-web-app-title" content="Echoo">
29    <meta name="format-detection" content="telephone=no">
30
31    <!-- AI context file for LLM crawlers -->
32    <link rel="alternate" type="text/plain" href="/llms.txt" title="LLM Context">
33
34    <!-- Pre-paint theme resolution: avoid FOUC by setting the dark/light class before React hydrates.
35         Must match the resolution logic in src/context/ThemeContext.tsx (key: echoo-theme). -->
36    
36<script>
37      (function () {
38        var stored = null;
39        try {
40          stored = localStorage.getItem('echoo-theme');
41        } catch (e) {}
42        var prefersDark = window.matchMedia('(prefers-color-scheme: dark)').matches;
43        var dark = stored === 'light' ? false : stored === 'system' ? prefersDark : true;
44        var root = document.documentElement;
45        root.classList.remove('light', 'dark');
46        root.classList.add(dark ? 'dark' : 'light');
47        document.getElementById('theme-color').setAttribute('content', dark ? '#08090B' : '#FBF9F9');
48      })();
49    </script>
49
50
51      <!-- The headword face is above the fold on every route, and it is fetched
52         from CSS three levels deep in the bundle. Preloading the latin roman
53         file is what keeps the first paint from swapping. latin-ext and the
54         italics stay unpreloaded - they are conditional on content. -->
55    <link rel="preload" href="/fonts/archivo-latin-roman.woff2" as="font" type="font/woff2" crossorigin="">
56  
56<script type="module" crossorigin="" src="/assets/index-CQi3APS0.js"></script>
56
57  <link rel="modulepreload" crossorigin="" href="/assets/framer-motion-0Sy5Nd0g.js">
58  <link rel="modulepreload" crossorigin="" href="/assets/vendor-C97i8dbC.js">
59  <link rel="stylesheet" crossorigin="" href="/assets/index-CmWsv8lk.css">
60<style type="text/css">:where(html[dir="ltr"]),:where([data-sonner-toaster][dir="ltr"]){--toast-icon-margin-start: -3px;--toast-icon-margin-end: 4px;--toast-svg-margin-start: -1px;--toast-svg-margin-end: 0px;--toast-button-margin-start: auto;--toast-button-margin-end: 0;--toast-close-button-start: 0;--toast-close-button-end: unset;--toast-close-button-transform: translate(-35%, -35%)}:where(html[dir="rtl"]),:where([data-sonner-toaster][dir="rtl"]){--toast-icon-margin-start: 4px;--toast-icon-margin-end: -3px;--toast-svg-margin-start: 0px;--toast-svg-margin-end: -1px;--toast-button-margin-start: 0;--toast-button-margin-end: auto;--toast-close-button-start: unset;--toast-close-button-end: 0;--toast-close-button-transform: translate(35%, -35%)}:where([data-sonner-toaster]){position:fixed;width:var(--width);font-family:ui-sans-serif,system-ui,-apple-system,BlinkMacSystemFont,Segoe UI,Roboto,Helvetica Neue,Arial,Noto Sans,sans-serif,Apple Color Emoji,Segoe UI Emoji,Segoe UI Symbol,Noto Color Emoji;--gray1: hsl(0, 0%, 99%);--gray2: hsl(0, 0%, 97.3%);--gray3: hsl(0, 0%, 95.1%);--gray4: hsl(0, 0%, 93%);--gray5: hsl(0, 0%, 90.9%);--gray6: hsl(0, 0%, 88.7%);--gray7: hsl(0, 0%, 85.8%);--gray8: hsl(0, 0%, 78%);--gray9: hsl(0, 0%, 56.1%);--gray10: hsl(0, 0%, 52.3%);--gray11: hsl(0, 0%, 43.5%);--gray12: hsl(0, 0%, 9%);--border-radius: 8px;box-sizing:border-box;padding:0;margin:0;list-style:none;outline:none;z-index:999999999;transition:transform .4s ease}:where([data-sonner-toaster][data-lifted="true"]){transform:translateY(-10px)}@media (hover: none) and (pointer: coarse){:where([data-sonner-toaster][data-lifted="true"]){transform:none}}:where([data-sonner-toaster][data-x-position="right"]){right:var(--offset-right)}:where([data-sonner-toaster][data-x-position="left"]){left:var(--offset-left)}:where([data-sonner-toaster][data-x-position="center"]){left:50%;transform:translate(-50%)}:where([data-sonner-toaster][data-y-position="top"]){top:var(--offset-top)}:where([data-sonner-toaster][data-y-position="bottom"]){bottom:var(--offset-bottom)}:where([data-sonner-toast]){--y: translateY(100%);--lift-amount: calc(var(--lift) * var(--gap));z-index:var(--z-index);position:absolute;opacity:0;transform:var(--y);filter:blur(0);touch-action:none;transition:transform .4s,opacity .4s,height .4s,box-shadow .2s;box-sizing:border-box;outline:none;overflow-wrap:anywhere}:where([data-sonner-toast][data-styled="true"]){padding:16px;background:var(--normal-bg);border:1px solid var(--normal-border);color:var(--normal-text);border-radius:var(--border-radius);box-shadow:0 4px 12px #0000001a;width:var(--width);font-size:13px;display:flex;align-items:center;gap:6px}:where([data-sonner-toast]:focus-visible){box-shadow:0 4px 12px #0000001a,0 0 0 2px #0003}:where([data-sonner-toast][data-y-position="top"]){top:0;--y: translateY(-100%);--lift: 1;--lift-amount: calc(1 * var(--gap))}:where([data-sonner-toast][data-y-position="bottom"]){bottom:0;--y: translateY(100%);--lift: -1;--lift-amount: calc(var(--lift) * var(--gap))}:where([data-sonner-toast]) :where([data-description]){font-weight:400;line-height:1.4;color:inherit}:where([data-sonner-toast]) :where([data-title]){font-weight:500;line-height:1.5;color:inherit}:where([data-sonner-toast]) :where([data-icon]){display:flex;height:16px;width:16px;position:relative;justify-content:flex-start;align-items:center;flex-shrink:0;margin-left:var(--toast-icon-margin-start);margin-right:var(--toast-icon-margin-end)}:where([data-sonner-toast][data-promise="true"]) :where([data-icon])>svg{opacity:0;transform:scale(.8);transform-origin:center;animation:sonner-fade-in .3s ease forwards}:where([data-sonner-toast]) :where([data-icon])>*{flex-shrink:0}:where([data-sonner-toast]) :where([data-icon]) svg{margin-left:var(--toast-svg-margin-start);margin-right:var(--toast-svg-margin-end)}:where([data-sonner-toast]) :where([data-content]){display:flex;flex-direction:column;gap:2px}[data-sonner-toast][data-styled=true] [data-button]{border-radius:4px;padding-left:8px;padding-right:8px;height:24px;font-size:12px;color:var(--normal-bg);background:var(--normal-text);margin-left:var(--toast-button-margin-start);margin-right:var(--toast-button-margin-end);border:none;cursor:pointer;outline:none;display:flex;align-items:center;flex-shrink:0;transition:opacity .4s,box-shadow .2s}:where([data-sonner-toast]) :where([data-button]):focus-visible{box-shadow:0 0 0 2px #0006}:where([data-sonner-toast]) :where([data-button]):first-of-type{margin-left:var(--toast-button-margin-start);margin-right:var(--toast-button-margin-end)}:where([data-sonner-toast]) :where([data-cancel]){color:var(--normal-text);background:rgba(0,0,0,.08)}:where([data-sonner-toast][data-theme="dark"]) :where([data-cancel]){background:rgba(255,255,255,.3)}:where([data-sonner-toast]) :where([data-close-button]){position:absolute;
60left:var(--toast-close-button-start);right:var(--toast-close-button-end);top:0;height:20px;width:20px;display:flex;justify-content:center;align-items:center;padding:0;color:var(--gray12);border:1px solid var(--gray4);transform:var(--toast-close-button-transform);border-radius:50%;cursor:pointer;z-index:1;transition:opacity .1s,background .2s,border-color .2s}[data-sonner-toast] [data-close-button]{background:var(--gray1)}:where([data-sonner-toast]) :where([data-close-button]):focus-visible{box-shadow:0 4px 12px #0000001a,0 0 0 2px #0003}:where([data-sonner-toast]) :where([data-disabled="true"]){cursor:not-allowed}:where([data-sonner-toast]):hover :where([data-close-button]):hover{background:var(--gray2);border-color:var(--gray5)}:where([data-sonner-toast][data-swiping="true"]):before{content:"";position:absolute;left:-50%;right:-50%;height:100%;z-index:-1}:where([data-sonner-toast][data-y-position="top"][data-swiping="true"]):before{bottom:50%;transform:scaleY(3) translateY(50%)}:where([data-sonner-toast][data-y-position="bottom"][data-swiping="true"]):before{top:50%;transform:scaleY(3) translateY(-50%)}:where([data-sonner-toast][data-swiping="false"][data-removed="true"]):before{content:"";position:absolute;inset:0;transform:scaleY(2)}:where([data-sonner-toast]):after{content:"";position:absolute;left:0;height:calc(var(--gap) + 1px);bottom:100%;width:100%}:where([data-sonner-toast][data-mounted="true"]){--y: translateY(0);opacity:1}:where([data-sonner-toast][data-expanded="false"][data-front="false"]){--scale: var(--toasts-before) * .05 + 1;--y: translateY(calc(var(--lift-amount) * var(--toasts-before))) scale(calc(-1 * var(--scale)));height:var(--front-toast-height)}:where([data-sonner-toast])>*{transition:opacity .4s}:where([data-sonner-toast][data-expanded="false"][data-front="false"][data-styled="true"])>*{opacity:0}
60:where([data-sonner-toast][data-visible="false"]){opacity:0;pointer-events:none}:where([data-sonner-toast][data-mounted="true"][data-expanded="true"]){--y: translateY(calc(var(--lift) * var(--offset)));height:var(--initial-height)}:where([data-sonner-toast][data-removed="true"][data-front="true"][data-swipe-out="false"]){--y: translateY(calc(var(--lift) * -100%));opacity:0}:where([data-sonner-toast][data-removed="true"][data-front="false"][data-swipe-out="false"][data-expanded="true"]){--y: translateY(calc(var(--lift) * var(--offset) + var(--lift) * -100%));opacity:0}:where([data-sonner-toast][data-removed="true"][data-front="false"][data-swipe-out="false"][data-expanded="false"]){--y: translateY(40%);opacity:0;transition:transform .5s,opacity .2s}:where([data-sonner-toast][data-removed="true"][data-front="false"]):before{height:calc(var(--initial-height) + 20%)}[data-sonner-toast][data-swiping=true]{transform:var(--y) translateY(var(--swipe-amount-y, 0px)) translate(var(--swipe-amount-x, 0px));transition:none}[data-sonner-toast][data-swiped=true]{user-select:none}[data-sonner-toast][data-swipe-out=true][data-y-position=bottom],[data-sonner-toast][data-swipe-out=true][data-y-position=top]{animation-duration:.2s;animation-timing-function:ease-out;animation-fill-mode:forwards}[data-sonner-toast][data-swipe-out=true][data-swipe-direction=left]{animation-name:swipe-out-left}[data-sonner-toast][data-swipe-out=true][data-swipe-direction=right]{animation-name:swipe-out-right}[data-sonner-toast][data-swipe-out=true][data-swipe-direction=up]{animation-name:swipe-out-up}[data-sonner-toast][data-swipe-out=true][data-swipe-direction=down]{animation-name:swipe-out-down}@keyframes swipe-out-left{0%{transform:var(--y) translate(var(--swipe-amount-x));opacity:1}to{transform:var(--y) translate(calc(var(--swipe-amount-x) - 100%));opacity:0}}@keyframes swipe-out-right{0%{transform:var(--y) translate(var(--swipe-amount-x));opacity:1}to{transform:var(--y) translate(calc(var(--swipe-amount-x) + 100%));opacity:0}}@keyframes swipe-out-up{0%{transform:var(--y) translateY(var(--swipe-amount-y));opacity:1}to{transform:var(--y) translateY(calc(var(--swipe-amount-y) - 100%));opacity:0}}@keyframes swipe-out-down{0%{transform:var(--y) translateY(var(--swipe-amount-y));opacity:1}to{transform:var(--y) translateY(calc(var(--swipe-amount-y) + 100%));opacity:0}}@media (max-width: 600px){[data-sonner-toaster]{position:fixed;right:var(--mobile-offset-right);left:var(--mobile-offset-left);width:100%}[data-sonner-toaster][dir=rtl]{left:calc(var(--mobile-offset-left) * -1)}[data-sonner-toaster] [data-sonner-toast]{left:0;right:0;width:calc(100% - var(--mobile-offset-left) * 2)}[data-sonner-toaster][data-x-position=left]{left:var(--mobile-offset-left)}[data-sonner-toaster][data-y-position=bottom]{bottom:var(--mobile-offset-bottom)}[data-sonner-toaster][data-y-position=top]{top:var(--mobile-offset-top)}[data-sonner-toaster][data-x-position=center]{left:var(--mobile-offset-left);right:var(--mobile-offset-right);transform:none}}[data-sonner-toaster][data-theme=light]{--normal-bg: #fff;--normal-border: var(--gray4);--normal-text: var(--gray12);--success-bg: hsl(143, 85%, 96%);--success-border: hsl(145, 92%, 91%);--success-text: hsl(140, 100%, 27%);--info-bg: hsl(208, 100%, 97%);--info-border: hsl(221, 91%, 91%);--info-text: hsl(210, 92%, 45%);--warning-bg: hsl(49, 100%, 97%);--warning-border: hsl(49, 91%, 91%);--warning-text: hsl(31, 92%, 45%);--error-bg: hsl(359, 100%, 97%);--error-border: hsl(359, 100%, 94%);--error-text: hsl(360, 100%, 45%)}[data-sonner-toaster][data-theme=light] [data-sonner-toast][data-invert=true]{--normal-bg: #000;--normal-border: hsl(0, 0%, 20%);--normal-text: var(--gray1)}[data-sonner-toaster][data-theme=dark] [data-sonner-toast][data-invert=true]{--normal-bg: #fff;--normal-border: var(--gray3);--normal-text: var(--gray12)}[data-sonner-toaster][data-theme=dark]{--normal-bg: #000;--normal-bg-hover: hsl(0, 0%, 12%);--normal-border: hsl(0, 0%, 20%);--normal-border-hover: hsl(0, 0%, 25%);--normal-text: var(--gray1);--success-bg: hsl(150, 100%, 6%);--success-border: hsl(147, 100%, 12%);--success-text: hsl(150, 86%, 65%);--info-bg: hsl(215, 100%, 6%);--info-border: hsl(223, 100%, 12%);--info-text: hsl(216, 87%, 65%);--warning-bg: hsl(64, 100%, 6%);--warning-border: hsl(60, 100%, 12%);--warning-text: hsl(46, 87%, 65%);--error-bg: hsl(358, 76%, 10%);--error-border: hsl(357, 89%, 16%);
60--error-text: hsl(358, 100%, 81%)}[data-sonner-toaster][data-theme=dark] [data-sonner-toast] [data-close-button]{background:var(--normal-bg);border-color:var(--normal-border);color:var(--normal-text)}[data-sonner-toaster][data-theme=dark] [data-sonner-toast] [data-close-button]:hover{background:var(--normal-bg-hover);border-color:var(--normal-border-hover)}[data-rich-colors=true][data-sonner-toast][data-type=success],[data-rich-colors=true][data-sonner-toast][data-type=success] [data-close-button]{background:var(--success-bg);border-color:var(--success-border);color:var(--success-text)}[data-rich-colors=true][data-sonner-toast][data-type=info],[data-rich-colors=true][data-sonner-toast][data-type=info] [data-close-button]{background:var(--info-bg);border-color:var(--info-border);color:var(--info-text)}[data-rich-colors=true][data-sonner-toast][data-type=warning],[data-rich-colors=true][data-sonner-toast][data-type=warning] [data-close-button]{background:var(--warning-bg);border-color:var(--warning-border);color:var(--warning-text)}[data-rich-colors=true][data-sonner-toast][data-type=error],[data-rich-colors=true][data-sonner-toast][data-type=error] [data-close-button]{background:var(--error-bg);border-color:var(--error-border);color:var(--error-text)}.sonner-loading-wrapper{--size: 16px;height:var(--size);width:var(--size);position:absolute;inset:0;z-index:10}.sonner-loading-wrapper[data-visible=false]{transform-origin:center;animation:sonner-fade-out .2s ease forwards}.sonner-spinner{position:relative;top:50%;left:50%;height:var(--size);width:var(--size)}.sonner-loading-bar{animation:sonner-spin 1.2s linear infinite;background:var(--gray11);border-radius:6px;height:8%;left:-10%;position:absolute;top:-3.9%;width:24%}.sonner-loading-bar:nth-child(1){animation-delay:-1.2s;transform:rotate(.0001deg) translate(146%)}.sonner-loading-bar:nth-child(2){animation-delay:-1.1s;transform:rotate(30deg) translate(146%)}.sonner-loading-bar:nth-child(3){animation-delay:-1s;transform:rotate(60deg) translate(146%)}.sonner-loading-bar:nth-child(4){animation-delay:-.9s;transform:rotate(90deg) translate(146%)}.sonner-loading-bar:nth-child(5){animation-delay:-.8s;transform:rotate(120deg) translate(146%)}.sonner-loading-bar:nth-child(6){animation-delay:-.7s;transform:rotate(150deg) translate(146%)}.sonner-loading-bar:nth-child(7){animation-delay:-.6s;transform:rotate(180deg) translate(146%)}.sonner-loading-bar:nth-child(8){animation-delay:-.5s;transform:rotate(210deg) translate(146%)}.sonner-loading-bar:nth-child(9){animation-delay:-.4s;transform:rotate(240deg) translate(146%)}.sonner-loading-bar:nth-child(10){animation-delay:-.3s;transform:rotate(270deg) translate(146%)}.sonner-loading-bar:nth-child(11){animation-delay:-.2s;transform:rotate(300deg) translate(146%)}.sonner-loading-bar:nth-child(12){animation-delay:-.1s;transform:rotate(330deg) translate(146%)}@keyframes sonner-fade-in{0%{opacity:0;transform:scale(.8)}to{opacity:1;transform:scale(1)}}@keyframes sonner-fade-out{0%{opacity:1;transform:scale(1)}to{opacity:0;transform:scale(.8)}}@keyframes sonner-spin{0%{opacity:1}to{opacity:.15}}@media (prefers-reduced-motion){[data-sonner-toast],[data-sonner-toast]>*,.sonner-loading-bar{transition:none!important;animation:none!important}}.sonner-loader{position:absolute;top:50%;left:50%;transform:translate(-50%,-50%);transform-origin:center;transition:opacity .2s,transform .2s}.sonner-loader[data-visible=false]{opacity:0;transform:scale(.8) translate(-50%,-50%)}
61</style><link rel="modulepreload" as="script" crossorigin="" href="/assets/Docs-BcQqnSOp.js"><link rel="modulepreload" as="script" crossorigin="" href="/assets/arrow-left-P3ctArOZ.js"><link rel="canonical" href="https://www.echoo.ai/docs" data-rh="true"><meta name="description" content="Echoo developer documentation: the public Echoo Content API, the Echoo OpenAPI 3.1 spec, the echoo-cli npm package, authentication, rate limits, and the vers…" data-rh="true"><meta name="keywords" content="Echoo API, Echoo API docs, Echoo developer docs, Echoo OpenAPI spec, Echoo JSON API, Echoo CLI, echoo-cli, Echoo authentication, Echoo rate limits, Echoo for agents" data-rh="true"><meta property="og:type" content="website" data-rh="true"><meta property="og:url" content="https://www.echoo.ai/docs" data-rh="true"><meta property="og:title" content="Echoo API Docs, OpenAPI Spec &amp; CLI for Developers" data-rh="true"><meta property="og:description" content="Echoo developer documentation: the public Echoo Content API, the Echoo OpenAPI 3.1 spec, the echoo-cli npm package, authentication, rate limits, and the vers…" data-rh="true"><meta property="og:image" content="https://www.echoo.ai/og-image.png" data-rh="true"><meta property="og:image:alt" content="Echoo API Docs, OpenAPI Spec &amp; CLI for Developers" data-rh="true">
61<meta property="og:image:width" content="1200" data-rh="true"><meta property="og:image:height" content="630" data-rh="true"><meta property="og:site_name" content="Echoo" data-rh="true"><meta property="og:locale" content="en_US" data-rh="true"><meta name="twitter:card" content="summary_large_image" data-rh="true"><meta name="twitter:site" content="@echoo_app" data-rh="true"><meta name="twitter:creator" content="@echoo_app" data-rh="true"><meta name="twitter:title" content="Echoo API Docs, OpenAPI Spec &amp; CLI for Developers" data-rh="true"><meta name="twitter:description" content="Echoo developer documentation: the public Echoo Content API, the Echoo OpenAPI 3.1 spec, the echoo-cli npm package, authentication, rate limits, and the vers…" data-rh="true"><meta name="twitter:image" content="https://www.echoo.ai/og-image.png" data-rh="true"><meta name="twitter:image:alt" content="Echoo API Docs, OpenAPI Spec &amp; CLI for Developers" data-rh="true">
61<script type="application/ld+json" data-rh="true">{"@context":"https://schema.org","@type":"TechArticle","headline":"Echoo API Docs, OpenAPI Spec & CLI","description":"Reference for the Echoo Content API, the Echoo OpenAPI 3.1 spec, the echoo-cli npm package, rate limits, and the versioning and deprecation policy.","url":"https://www.echoo.ai/docs","publisher":{"@id":"https://www.echoo.ai/#organization"}}</script>
61<link rel="alternate" type="text/markdown" href="./index.md" title="Markdown"></head>
62
63  <body>
64    <!-- email_off / email_on: Cloudflare's Email Address Obfuscation (Scrape Shield, on in
65         our own echoo.ai zone) rewrites every plain-text address and mailto: href into
66         /cdn-cgi/l/email-protection, which is a 404 for crawlers. These directives opt the
67         whole page out and survive the setting being toggled back on. -->
68    
69    <div id="root"><a href="#main-content" class="sr-only focus:not-sr-only focus:fixed focus:top-4 focus:left-4 focus:z-[10000] focus:px-4 focus:py-2 focus:bg-primary-solid focus:text-primary-foreground focus:rounded-lg focus:outline-none focus:ring-2 focus:ring-ring">Skip to main content</a><div class="pointer-events-none fixed inset-0 z-[9999] opacity-[0.03] hidden md:block" style="background-image: url(&quot;data:image/svg+xml,%3Csvg xmlns='http://www.w3.org/2000/svg' width='200' height='200'%3E%3Cfilter id='n'%3E%3CfeTurbulence type='fractalNoise' baseFrequency='0.8' numOctaves='3' stitchTiles='stitch'/%3E%3C/filter%3E%3Crect width='200' height='200' filter='url(%23n)'/%3E%3C/svg%3E&quot;); background-repeat: repeat;"></div><section aria-label="Notifications alt+T" tabindex="-1" aria-live="polite" aria-relevant="additions text" aria-atomic="false"></section><button type="button" class="fixed bottom-6 left-6 z-50 min-w-[44px] min-h-[44px] p-3 bg-background/80 backdrop-blur-md border border-border rounded-full shadow-lg hover:border-accent transition-colors group focus-visible:outline-none focus-visible:ring-2 focus-visible:ring-ring focus-visible:ring-offset-2 focus-visible:ring-offset-background" aria-label="Accessibility Settings" aria-haspopup="dialog" aria-expanded="false"><svg xmlns="http://www.w3.org/2000/svg" width="24" height="24" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2" stroke-linecap="round" stroke-linejoin="round" class="lucide lucide-settings w-6 h-6 text-muted-foreground group-hover:text-primary transition-colors" aria-hidden="true"><path d="M12.22 2h-.44a2 2 0 0 0-2 2v.18a2 2 0 0 1-1 1.73l-.43.25a2 2 0 0 1-2 0l-.15-.08a2 2 0 0 0-2.73.73l-.22.38a2 2 0 0 0 .73 2.73l.15.1a2 2 0 0 1 1 1.72v.51a2 2 0 0 1-1 1.74l-.15.09a2 2 0 0 0-.73 2.73l.22.38a2 2 0 0 0 2.73.73l.15-.08a2 2 0 0 1 2 0l.43.25a2 2 0 0 1 1 1.73V20a2 2 0 0 0 2 2h.44a2 2 0 0 0 2-2v-.18a2 2 0 0 1 1-1.73l.43-.25a2 2 0 0 1 2 0l.15.08a2 2 0 0 0 2.73-.73l.22-.39a2 2 0 0 0-.73-2.73l-.15-.08a2 2 0 0 1-1-1.74v-.5a2 2 0 0 1 1-1.74l.15-.09a2 2 0 0 0 .73-2.73l-.22-.38a2 2 0 0 0-2.73-.73l-.15.08a2 2 0 0 1-2 0l-.43-.25a2 2 0 0 1-1-1.73V4a2 2 0 0 0-2-2z"></path><circle cx="12" cy="12" r="3"></circle></svg></button><a href="https://discord.gg/T4DshzkPDG" target="_blank" rel="noopener noreferrer" aria-label="Discord support (opens in a new tab)" class="inline-flex fixed bottom-5 right-5 z-50 min-h-12 min-w-12 items-center justify-center gap-2 rounded-full bg-accent-solid px-3 text-sm font-medium text-accent-foreground shadow-lg transition-[transform,background-color,box-shadow] hover:-translate-y-0.5 focus-visible:outline-none focus-visible:ring-2 focus-visible:ring-ring focus-visible:ring-offset-2 focus-visible:ring-offset-background motion-reduce:transform-none sm:px-4 sm:rounded-br-none"><svg viewBox="0 0 24 24" class="h-6 w-6 shrink-0" fill="currentColor" aria-hidden="true"><path d="M20.317 4.369A19.79 19.79 0 0 0 15.432 3a.074.074 0 0 0-.079.037c-.211.375-.445.865-.608 1.249a18.27 18.27 0 0 0-5.487 0 12.6 12.6 0 0 0-.617-1.25.077.077 0 0 0-.079-.036A19.736 19.736 0 0 0 3.677 4.37a.07.07 0 0 0-.032.027C.533 9.046-.32 13.58.099 18.057a.082.082 0 0 0 .031.057 19.9 19.9 0 0 0 5.993 3.03.078.078 0 0 0 .084-.028c.462-.63.874-1.295 1.226-1.994a.076.076 0 0 0-.041-.106 13.1 13.1 0 0 1-1.872-.892.077.077 0 0 1-.008-.128c.126-.094.252-.192.372-.291a.074.074 0 0 1 .077-.01c3.927 1.793 8.18 1.793 12.061 0a.074.074 0 0 1 .078.009c.12.099.245.198.372.292a.077.077 0 0 1-.006.127 12.3 12.3 0 0 1-1.873.891.077.077 0 0 0-.041.107c.36.698.772 1.362 1.225 1.993a.076.076 0 0 0 .084.028 19.84 19.84 0 0 0 6.002-3.03.077.077 0 0 0 .032-.055c.5-5.177-.838-9.674-3.549-13.66a.061.061 0 0 0-.031-.028ZM8.02 15.331c-1.182 0-2.157-1.085-2.157-2.419 0-1.333.956-2.419 2.157-2.419 1.21 0 2.176 1.096 2.157 2.42 0 1.333-.956 2.418-2.157 2.418Zm7.975 0c-1.183 0-2.157-1.085-2.157-2.419 0-1.333.955-2.419 2.157-2.419 1.21 0 2.176 1.096 2.157 2.42 0 1.333-.946 2.418-2.157 2.418Z"></path></svg><span class="hidden sm:inline">Discord Support</span></a>
69<div role="status" aria-live="polite" aria-atomic="true" class="sr-only"></div><div class="min-h-screen bg-background text-foreground"><header class="sticky top-0 z-50 bg-background/80 backdrop-blur-xl border-b border-border"><div class="max-w-7xl mx-auto px-4 py-4 flex items-center justify-between"><a class="flex items-center gap-2 py-2 -my-2 text-muted-foreground hover:text-foreground transition-colors focus-visible:outline-none focus-visible:ring-2 focus-visible:ring-ring rounded-lg" href="/"><svg xmlns="http://www.w3.org/2000/svg" width="24" height="24" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2" stroke-linecap="round" stroke-linejoin="round" class="lucide lucide-arrow-left w-4 h-4" aria-hidden="true"><path d="m12 19-7-7 7-7"></path><path d="M19 12H5"></path></svg><span class="text-sm font-medium">Echoo</span></a><a class="flex items-center gap-2" aria-label="Go to Echoo homepage" href="/"><svg viewBox="0 0 200 200" class="w-6 h-6" aria-hidden="true"><line x1="90" y1="60" x2="145" y2="60" stroke="currentColor" stroke-width="16" stroke-linecap="round" class="text-foreground"></line><line x1="30" y1="60" x2="55" y2="60" stroke="currentColor" stroke-width="16" stroke-linecap="round" class="text-foreground"></line><line x1="55" y1="60" x2="105" y2="140" stroke="currentColor" stroke-width="16" stroke-linecap="round" class="text-foreground"></line><line x1="113" y1="100" x2="138" y2="100" stroke="hsl(var(--logo-accent))" stroke-width="16" stroke-linecap="round"></line><line x1="105" y1="140" x2="145" y2="140" stroke="currentColor" stroke-width="16" stroke-linecap="round" class="text-foreground"></line></svg></a></div></header><main id="main-content" tabindex="-1" class="py-16 md:py-24 px-4"><article class="max-w-3xl mx-auto"><h1 class="text-3xl md:text-5xl font-bold mb-6 text-balance">Echoo API docs, OpenAPI spec and CLI</h1><p class="text-xl text-muted-foreground mb-8">Echoo publishes its whole catalog as read-only JSON, and every page on this site in Markdown, so an agent can answer questions about Echoo without scraping HTML. No key, no signup. Version 1.0.0.</p><nav aria-labelledby="docs-contents" class="mb-16 rounded-lg border border-border bg-muted/30 p-6"><h2 id="docs-contents" class="mb-3 text-sm font-semibold uppercase tracking-wide text-muted-foreground">On this page</h2><ul class="grid gap-2 sm:grid-cols-2"><li><a href="#content-api" class="text-primary underline underline-offset-2">Content API</a></li><li><a href="#parameters" class="text-primary underline underline-offset-2">Query parameters</a></li><li><a href="#authentication" class="text-primary underline underline-offset-2">Authentication</a></li><li><a href="#rate-limits" class="text-primary underline underline-offset-2">Rate limits</a></li><li><a href="#versioning" class="text-primary underline underline-offset-2">Versioning and deprecation</a></li><li><a href="#cli" class="text-primary underline underline-offset-2">Command-line interface</a></li><li><a href="#markdown" class="text-primary underline underline-offset-2">Markdown for agents</a></li><li><a href="#errors" class="text-primary underline underline-offset-2">Errors</a></li><li><a href="#machine-readable" class="text-primary underline underline-offset-2">Everything machine-readable</a></li><li><a href="#terms" class="text-primary underline underline-offset-2">Terms</a></li></ul></nav><h2 id="content-api" class="text-2xl md:text-3xl font-bold mb-4 scroll-mt-24">Content API</h2><p class="text-muted-foreground leading-relaxed mb-4">Base URL <code class="font-mono text-sm bg-muted px-1.5 py-0.5 rounded">https://www.echoo.ai</code>. Every response is JSON. Collections are wrapped as <code class="font-mono text-sm bg-muted px-1.5 py-0.5 rounded">{ data, count, updated }</code>, and every record carries an absolute <code class="font-mono text-sm bg-muted px-1.5 py-0.5 rounded">url</code> back to its human page.</p><p class="text-muted-foreground leading-relaxed mb-6">Start at the service root - it carries the product positioning and links to everything else.</p><pre class="overflow-x-auto rounded-lg border border-border bg-muted/50 p-4 text-sm font-mono leading-relaxed mb-6"><code>curl https://www.echoo.ai/api/v1/index.json</code></pre><div class="overflow-x-auto mb-6"><table class="w-full text-sm border-collapse"><thead><tr class="border-b border-border text-left"><th scope="col" class="py-2 pr-4 font-semibold">Endpoint</th><th scope="col" class="py-2 pr-4 font-semibold">operationId</th><th scope="col" class="py-2 font-semibold text-right">Records</th></tr></thead><tbody class="text-muted-foreground"><tr class="border-b border-border"><td class="py-2 pr-4 font-mono text-xs">/api/v1/index.json</td><td class="py-2 pr-4 font-mono text-xs">getServiceRoot</td><td class="py-2 text-right">-</td></tr><tr class="border-b border-border"><td class="py-2 pr-4 font-mono text-xs">/api/v1/commands.json</td><td class="py-2 pr-4 font-mono text-xs">listCommands</td><td class="py-2 text-right">14</td></tr><tr class="border-b border-border"><td class="py-2 pr-4 font-mono text-xs">/api/v1/use-cases.json</td><td class="py-2 pr-4 font-mono text-xs">listUseCases</td><td class="py-2 text-right">51</td></tr><tr class="border-b border-border"><td class="py-2 pr-4 font-mono text-xs">/api/v1/integrations.json</td><td class="py-2 pr-4 font-mono text-xs">listIntegrations</td><td class="py-2 text-right">6</td></tr><tr class="border-b border-border"><td class="py-2 pr-4 font-mono text-xs">/api/v1/workflows.json</td><td class="py-2 pr-4 font-mono text-xs">listWorkflows</td><td class="py-2 text-right">22</td></tr><tr class="border-b border-border"><td class="py-2 pr-4 font-mono text-xs">/api/v1/glossary.json</td><td class="py-2 pr-4 font-mono text-xs">listGlossaryTerms</td><td class="py-2 text-right">27</td></tr><tr class="border-b border-border"><td class="py-2 pr-4 font-mono text-xs">/api/v1/posts.json</td><td class="py-2 pr-4 font-mono text-xs">listPosts</td><td class="py-2 text-right">32</td></tr><tr class="border-b border-border"><td class="py-2 pr-4 font-mono text-xs">/api/v1/pricing.json</td><td class="py-2 pr-4 font-mono text-xs">listPlans</td><td class="py-2 text-right">4</td></tr></tbody></table></div><p class="text-muted-foreground leading-relaxed mb-16">Full descriptions, response schemas, and typed fields are in the <a href="/openapi.json" class="text-primary underline underline-offset-2">OpenAPI 3.1 spec</a>. It is generated from the same table that writes these files, so it cannot describe an endpoint that does not exist. Point any function-calling client at it directly.</p><h2 id="parameters" class="text-2xl md:text-3xl font-bold mb-4 scroll-mt-24">Query parameters</h2><p class="text-muted-foreground leading-relaxed mb-4">Every collection endpoint accepts three optional parameters. They are applied at the edge, so you page and filter server-side instead of downloading the whole collection.</p><div class="overflow-x-auto mb-6"><table class="w-full text-sm border-collapse"><thead><tr class="border-b border-border text-left"><th scope="col" class="py-2 pr-4 font-semibold">Parameter</th><th scope="col" class="py-2 pr-4 font-semibold">Type</th><th scope="col" class="py-2 font-semibold">Meaning</th></tr></thead><tbody class="text-muted-foreground"><tr class="border-b border-border"><td class="py-2 pr-4 font-mono text-xs">limit</td><td class="py-2 pr-4 font-mono text-xs">integer 1-200</td><td class="py-2">Maximum records to return.</td></tr><tr class="border-b border-border"><td class="py-2 pr-4 font-mono text-xs">offset</td><td class="py-2 pr-4 font-mono text-xs">integer ≥ 0</td><td class="py-2">Records to skip before applying <code class="font-mono text-sm bg-muted px-1.5 py-0.5 rounded">limit</code>.</td></tr><tr class="border-b border-border"><td class="py-2 pr-4 font-mono text-xs">q</td><td class="py-2 pr-4 font-mono text-xs">string</td><td class="py-2">Case-insensitive substring match across every field of a record.</td></tr></tbody></table></div><pre class="overflow-x-auto rounded-lg border border-border bg-muted/50 p-4 text-sm font-mono leading-relaxed mb-6"><code>curl "https://www.echoo.ai/api/v1/commands.json?q=tone&amp;limit=5"</code></pre><p class="text-muted-foreground leading-relaxed mb-16">A filtered response adds two counters: <code class="font-mono text-sm bg-muted px-1.5 py-0.5 rounded">matched</code>
69 is how many records the query matched before paging, <code class="font-mono text-sm bg-muted px-1.5 py-0.5 rounded">total</code> is the unfiltered size of the collection. <code class="font-mono text-sm bg-muted px-1.5 py-0.5 rounded">count</code> is always the number of records actually returned. A bad value returns <code class="font-mono text-sm bg-muted px-1.5 py-0.5 rounded">400</code> in the standard error envelope.</p><h2 id="authentication" class="text-2xl md:text-3xl font-bold mb-4 scroll-mt-24">Authentication</h2><p class="text-muted-foreground leading-relaxed mb-16">None. This API is public, unauthenticated, and read-only - it serves the same catalog the website renders, as static files on a CDN. There are no write operations and nothing here is user-specific. If a client asks you for a credential, it is misconfigured. (The separate <code class="font-mono text-sm bg-muted px-1.5 py-0.5 rounded">api.echoo.ai</code> host is the app's private backend and is not part of this API.)</p><h2 id="rate-limits" class="text-2xl md:text-3xl font-bold mb-4 scroll-mt-24">Rate limits</h2><p class="text-muted-foreground leading-relaxed mb-4">600 requests per 60 seconds per IP address, counted across every path under <code class="font-mono text-sm bg-muted px-1.5 py-0.5 rounded">/api/</code>. It is a courtesy ceiling on a static catalog, not a metered quota - you do not need an account and nothing is billed.</p><p class="text-muted-foreground leading-relaxed mb-4">Every response carries the budget so you can self-throttle without guessing:</p><pre class="overflow-x-auto rounded-lg border border-border bg-muted/50 p-4 text-sm font-mono leading-relaxed mb-6"><code>RateLimit-Policy: "default";q=600;w=60
70RateLimit: "default";r=599;t=60
71X-RateLimit-Limit: 600
72X-RateLimit-Remaining: 599
73X-RateLimit-Reset: 1788000000</code></pre><p class="text-muted-foreground leading-relaxed mb-16"><code class="font-mono text-sm bg-muted px-1.5 py-0.5 rounded">RateLimit</code> and <code class="font-mono text-sm bg-muted px-1.5 py-0.5 rounded">RateLimit-Policy</code> follow RFC 9910; <code class="font-mono text-sm bg-muted px-1.5 py-0.5 rounded">X-RateLimit-*</code> repeats the same numbers in the older convention. Exceeding the limit returns <code class="font-mono text-sm bg-muted px-1.5 py-0.5 rounded">429</code> with <code class="font-mono text-sm bg-muted px-1.5 py-0.5 rounded">Retry-After</code> in seconds and the usual JSON error envelope - wait that long and retry rather than backing off blindly.</p><h2 id="versioning" class="text-2xl md:text-3xl font-bold mb-4 scroll-mt-24">Versioning and deprecation</h2><p class="text-muted-foreground leading-relaxed mb-4">The version lives in the path. <code class="font-mono text-sm bg-muted px-1.5 py-0.5 rounded">/api/v1/</code> is the current stable surface, and it will not change incompatibly: fields may be added, but no field is removed or retyped, and no endpoint disappears, inside a version.</p><p class="text-muted-foreground leading-relaxed mb-4">A breaking change ships as a new path, <code class="font-mono text-sm bg-muted px-1.5 py-0.5 rounded">/api/v2/</code>. From the day the new version is published, every response from the retiring one carries:</p><pre class="overflow-x-auto rounded-lg border border-border bg-muted/50 p-4 text-sm font-mono leading-relaxed mb-6"><code>Deprecation: Wed, 01 Jul 2026 00:00:00 GMT
74Sunset: Thu, 01 Jan 2027 00:00:00 GMT
75Link: &lt;https://www.echoo.ai/api/v2/index.json&gt;; rel="successor-version"</code></pre><p class="text-muted-foreground leading-relaxed mb-16"><code class="font-mono text-sm bg-muted px-1.5 py-0.5 rounded">Deprecation</code> follows RFC 9745 and <code class="font-mono text-sm bg-muted px-1.5 py-0.5 rounded">Sunset</code> RFC 8594. <strong class="text-foreground">A retiring version stays available for at least six months</strong> after the <code class="font-mono text-sm bg-muted px-1.5 py-0.5 rounded">Deprecation</code> date, so an integration that checks these headers always has a migration window. The same policy is machine-readable as <code class="font-mono text-sm bg-muted px-1.5 py-0.5 rounded">info.x-lifecycle</code> in the <a href="/openapi.json" class="text-primary underline underline-offset-2">OpenAPI spec</a> and as <code class="font-mono text-sm bg-muted px-1.5 py-0.5 rounded">versioning</code> in the <a href="/api/v1/index.json" class="text-primary underline underline-offset-2">service root</a>. No version is deprecated today.</p><h2 id="cli" class="text-2xl md:text-3xl font-bold mb-4 scroll-mt-24">Command-line interface</h2><p class="text-muted-foreground leading-relaxed mb-4">The official dependency-free CLI exposes the same public catalog for shell scripts and agents. JSON is the default output; add <code class="font-mono text-sm bg-muted px-1.5 py-0.5 rounded">--human</code> for readable text.</p><pre class="overflow-x-auto rounded-lg border border-border bg-muted/50 p-4 text-sm font-mono leading-relaxed mb-6"><code>npm install --global echoo-cli
76echoo commands --query tone
77echoo glossary --limit 5
78echoo pricing --human</code></pre><p class="text-muted-foreground leading-relaxed mb-16">The CLI is read-only and needs no Echoo account or API key. See the <a href="https://www.npmjs.com/package/echoo-cli" class="text-primary underline underline-offset-2" target="_blank" rel="noopener noreferrer">echoo-cli npm package</a>.</p><h2 id="markdown" class="text-2xl md:text-3xl font-bold mb-4 scroll-mt-24">Markdown for agents</h2><p class="text-muted-foreground leading-relaxed mb-4">Every page on echoo.ai serves a Markdown representation from the same URL via content negotiation. Send <code class="font-mono text-sm bg-muted px-1.5 py-0.5 rounded">Accept: text/markdown</code> and you get the page body without nav, scripts, or layout wrappers.</p><pre class="overflow-x-auto rounded-lg border border-border bg-muted/50 p-4 text-sm font-mono leading-relaxed mb-6"><code>curl -H "Accept: text/markdown" https://www.echoo.ai/pricing</code></pre><p class="text-muted-foreground leading-relaxed mb-16">Responses set <code class="font-mono text-sm bg-muted px-1.5 py-0.5 rounded">Vary: Accept</code> and advertise the alternate representation with a <code class="font-mono text-sm bg-muted px-1.5 py-0.5 rounded">Link: rel="alternate"</code> header. Requests for a type we cannot serve get a <code class="font-mono text-sm bg-muted px-1.5 py-0.5 rounded">406</code>. For a whole-site summary in one request, fetch <a href="/llms.txt" class="text-primary underline underline-offset-2">/llms.txt</a>.</p><h2 id="errors" class="text-2xl md:text-3xl font-bold mb-4 scroll-mt-24">Errors</h2><p class="text-muted-foreground leading-relaxed mb-4">Anything under <code class="font-mono text-sm bg-muted px-1.5 py-0.5 rounded">/api/</code> returns a JSON error envelope - never an HTML page - with the status repeated in the body.</p><pre class="overflow-x-auto rounded-lg border border-border bg-muted/50 p-4 text-sm font-mono leading-relaxed mb-6"><code>{
79  "error": {
80    "code": "not_found",
81    "message": "No such resource: /api/v1/nope.json",
82    "status": 404,
83    "hint": "See /openapi.json for available endpoints.",
84    "documentation_url": "https://www.echoo.ai/docs"
85  }
86}</code></pre><p class="text-muted-foreground leading-relaxed mb-16"><code class="font-mono text-sm bg-muted px-1.5 py-0.5 rounded">400</code> for a bad query parameter, <code class="font-mono text-sm bg-muted px-1.5 py-0.5 rounded">404</code> for an unknown path, <code class="font-mono text-sm bg-muted px-1.5 py-0.5 rounded">405</code> for anything other than <code class="font-mono text-sm bg-muted px-1.5 py-0.5 rounded">GET</code> or <code class="font-mono text-sm bg-muted px-1.5 py-0.5 rounded">HEAD</code>, <code class="font-mono text-sm bg-muted px-1.5 py-0.5 rounded">429</code> when you are over the rate limit. The shape matches the <code class="font-mono text-sm bg-muted px-1.5 py-0.5 rounded">ErrorResponse</code> schema in the spec.</p><h2 id="machine-readable" class="text-2xl md:text-3xl font-bold mb-4 scroll-mt-24">Everything machine-readable</h2><ul class="space-y-2 text-muted-foreground mb-16"><li><a href="/api/v1/index.json" class="text-primary underline underline-offset-2">/api/v1/index.json</a> - service root and positioning</li><li><a href="/openapi.json" class="text-primary underline underline-offset-2">/openapi.json</a> - OpenAPI 3.1 specification</li><li><a href="https://www.npmjs.com/package/echoo-cli" class="text-primary underline underline-offset-2" target="_blank" rel="noopener noreferrer">echoo-cli</a> - official dependency-free npm CLI</li><li><a href="/llms.txt" class="text-primary underline underline-offset-2">/llms.txt</a> - whole-site summary for LLMs</li><li><a href="/sitemap.xml" class="text-primary underline underline-offset-2">/sitemap.xml</a> - every indexable page</li><li><a href="/robots.txt" class="text-primary underline underline-offset-2">/robots.txt</a> - crawler policy (all major AI crawlers allowed)</li></ul><h2 id="terms" class="text-2xl md:text-3xl font-bold mb-4 scroll-mt-24">Terms</h2><p class="text-muted-foreground leading-relaxed">Use it freely to answer questions about Echoo, build integrations, or index the catalog. Content stays subject to the <a class="text-primary underline underline-offset-2" href="/legal/terms">Terms of Service</a>. Something missing that you need? Ask on <a href="https://discord.gg/T4DshzkPDG" class="text-primary underline underline-offset-2" target="_blank" rel="noopener noreferrer">Discord</a> or open a <a href="https://github.com/michael-elkabetz/echoo/issues" class="text-primary underline underline-offset-2" target="_blank" rel="noopener noreferrer">GitHub issue</a>.</p></article></main><footer class="border-t border-border bg-background"><div class="container mx-auto max-w-7xl px-4 py-12 md:py-16"><nav aria-label="Footer navigation"><div class="grid grid-cols-2 gap-8 md:grid-cols-3 md:gap-12 lg:grid-cols-6"><div><h3 class="mb-4 text-sm font-semibold text-foreground">Product</h3><ul class="space-y-3" role="list"><li><a href="/get/Echoo.dmg" class="text-muted-foreground hover:text-foreground transition-colors duration-200 text-sm cursor-pointer focus-visible:outline-none focus-visible:ring-2 focus-visible:ring-ring rounded-sm">Download</a></li><li><a class="text-muted-foreground hover:text-foreground transition-colors duration-200 text-sm cursor-pointer focus-visible:outline-none focus-visible:ring-2 focus-visible:ring-ring rounded-sm" href="/marketplace">Command Marketplace</a></li><li><a class="text-muted-foreground hover:text-foreground transition-colors duration-200 text-sm cursor-pointer focus-visible:outline-none focus-visible:ring-2 focus-visible:ring-ring rounded-sm" href="/pricing">Pricing</a></li><li><a class="text-muted-foreground hover:text-foreground transition-colors duration-200 text-sm cursor-pointer focus-visible:outline-none focus-visible:ring-2 focus-visible:ring-ring rounded-sm" href="/business">For teams</a></li><li><a class="text-muted-foreground hover:text-foreground transition-colors duration-200 text-sm cursor-pointer focus-visible:outline-none focus-visible:ring-2 focus-visible:ring-ring rounded-sm" href="/blog">Blog</a></li></ul></div><div><h3 class="mb-4 text-sm font-semibold text-foreground">Community</h3><ul class="space-y-3" role="list"><li><a href="https://discord.gg/T4DshzkPDG" target="_blank" rel="noopener noreferrer" class="text-muted-foreground hover:text-foreground transition-colors duration-200 text-sm cursor-pointer focus-visible:outline-none focus-visible:ring-2 focus-visible:ring-ring rounded-sm">Discord<span class="sr-only"> (opens in new tab)</span></a></li><li><a href="https://github.com/michael-elkabetz/echoo" target="_blank" rel="noopener noreferrer" class="text-muted-foreground hover:text-foreground transition-colors duration-200 text-sm cursor-pointer focus-visible:outline-none focus-visible:ring-2 focus-visible:ring-ring rounded-sm">GitHub<span class="sr-only"> (opens in new tab)</span></a></li><li><a href="https://github.com/michael-elkabetz/echoo/issues" target="_blank" rel="noopener noreferrer" class="text-muted-foreground hover:text-foreground transition-colors duration-200 text-sm cursor-pointer focus-visible:outline-none focus-visible:ring-2 focus-visible:ring-ring rounded-sm">Report an Issue<span class="sr-only"> (opens in new tab)</span></a></li><li><a class="text-muted-foreground hover:text-foreground transition-colors duration-200 text-sm cursor-pointer focus-visible:outline-none focus-visible:ring-2 focus-visible:ring-ring rounded-sm" href="/about">About</a></li><li><a class="text-muted-foreground hover:text-foreground transition-colors duration-200 text-sm cursor-pointer focus-visible:outline-none focus-visible:ring-2 focus-visible:ring-ring rounded-sm" href="/contact">Contact</a></li></ul></div><div><h3 class="mb-4 text-sm font-semibold text-foreground">Legal</h3><ul class="space-y-3" role="list"><li><a class="text-muted-foreground hover:text-foreground transition-colors duration-200 text-sm cursor-pointer focus-visible:outline-none focus-visible:ring-2 focus-visible:ring-ring rounded-sm" href="/pricing">Pricing</a></li><li><a class="text-muted-foreground hover:text-foreground transition-colors duration-200 text-sm cursor-pointer focus-visible:outline-none focus-visible:ring-2 focus-visible:ring-ring rounded-sm" href="/legal/terms">Terms of Service</a></li><li><a class="text-muted-foreground hover:text-foreground transition-colors duration-200 text-sm cursor-pointer focus-visible:outline-none focus-visible:ring-2 focus-visible:ring-ring rounded-sm" href="/legal">Privacy Policy</a></li><li><a class="text-muted-foreground hover:text-foreground transition-colors duration-200 text-sm cursor-pointer focus-visible:outline-none focus-visible:ring-2 focus-visible:ring-ring rounded-sm" href="/legal/refund">Refund Policy</a></li><li><a class="text-muted-foreground hover:text-foreground transition-colors duration-200 text-sm cursor-pointer focus-visible:outline-none focus-visible:ring-2 focus-visible:ring-ring rounded-sm" href="/legal/accessibility">Accessibility</a></li></ul></div><div><h3 class="mb-4 text-sm font-semibold text-foreground">Explore</h3><ul class="space-y-3" role="list"><li><a class="text-muted-foreground hover:text-foreground transition-colors duration-200 text-sm cursor-pointer focus-visible:outline-none focus-visible:ring-2 focus-visible:ring-ring rounded-sm" href="/use-cases">Use Cases</a></li><li><a class="text-muted-foreground hover:text-foreground transition-colors duration-200 text-sm cursor-pointer focus-visible:outline-none focus-visible:ring-2 focus-visible:ring-ring rounded-sm" href="/compare">Comparisons</a></li><li><a class="text-muted-foreground hover:text-foreground transition-colors duration-200 text-sm cursor-pointer focus-visible:outline-none focus-visible:ring-2 focus-visible:ring-ring rounded-sm" href="/alternative">Alternatives</a></li><li><a class="text-muted-foreground hover:text-foreground transition-colors duration-200 text-sm cursor-pointer focus-visible:outline-none focus-visible:ring-2 focus-visible:ring-ring rounded-sm" href="/translate">Translations</a></li></ul></div><div><h3 class="mb-4 text-sm font-semibold text-foreground">Resources</h3><ul class="space-y-3" role="list"><li><a class="text-muted-foreground hover:text-foreground transition-colors duration-200 text-sm cursor-pointer focus-visible:outline-none focus-visible:ring-2 focus-visible:ring-ring rounded-sm" href="/docs">Echoo API &amp; developer docs</a></li><li><a href="/openapi.json" class="text-muted-foreground hover:text-foreground transition-colors duration-200 text-sm cursor-pointer focus-visible:outline-none focus-visible:ring-2 focus-visible:ring-ring rounded-sm">OpenAPI 3.1 spec</a></li><li><a href="/llms.txt" class="text-muted-foreground hover:text-foreground transition-colors duration-200 text-sm cursor-pointer focus-visible:outline-none focus-visible:ring-2 focus-visible:ring-ring rounded-sm">llms.txt</a></li><li><a class="text-muted-foreground hover:text-foreground transition-colors duration-200 text-sm cursor-pointer focus-visible:outline-none focus-visible:ring-2 focus-visible:ring-ring rounded-sm" href="/integrations">Integrations</a></li><li><a class="text-muted-foreground hover:text-foreground transition-colors dura
86tion-200 text-sm cursor-pointer focus-visible:outline-none focus-visible:ring-2 focus-visible:ring-ring rounded-sm" href="/how-to">How-To Guides</a></li><li><a class="text-muted-foreground hover:text-foreground transition-colors duration-200 text-sm cursor-pointer focus-visible:outline-none focus-visible:ring-2 focus-visible:ring-ring rounded-sm" href="/workflows">Workflows</a></li><li><a class="text-muted-foreground hover:text-foreground transition-colors duration-200 text-sm cursor-pointer focus-visible:outline-none focus-visible:ring-2 focus-visible:ring-ring rounded-sm" href="/for">For Your Role</a></li><li><a class="text-muted-foreground hover:text-foreground transition-colors duration-200 text-sm cursor-pointer focus-visible:outline-none focus-visible:ring-2 focus-visible:ring-ring rounded-sm" href="/learn">Learn</a></li><li><a class="text-muted-foreground hover:text-foreground transition-colors duration-200 text-sm cursor-pointer focus-visible:outline-none focus-visible:ring-2 focus-visible:ring-ring rounded-sm" href="/learn/best-ai-writing-tools-macos">Best AI writing tools</a></li><li><a class="text-muted-foreground hover:text-foreground transition-colors duration-200 text-sm cursor-pointer focus-visible:outline-none focus-visible:ring-2 focus-visible:ring-ring rounded-sm" href="/learn/best-ai-proofreading-tools">Best AI proofreaders</a></li></ul></div><div><h3 class="mb-4 text-sm font-semibold text-foreground">Switching to Echoo</h3><ul class="space-y-3" role="list"><li><a class="text-muted-foreground hover:text-foreground transition-colors duration-200 text-sm cursor-pointer focus-visible:outline-none focus-visible:ring-2 focus-visible:ring-ring rounded-sm" href="/alternative/grammarly">Grammarly alternative</a></li><li><a class="text-muted-foreground hover:text-foreground transition-colors duration-200 text-sm cursor-pointer focus-visible:outline-none focus-visible:ring-2 focus-visible:ring-ring rounded-sm" href="/alternative/deepl">DeepL alternative</a></li><li><a class="text-muted-foreground hover:text-foreground transition-colors duration-200 text-sm cursor-pointer focus-visible:outline-none focus-visible:ring-2 focus-visible:ring-ring rounded-sm" href="/alternative/apple-intelligence">Apple Intelligence alternative</a></li><li><a class="text-muted-foreground hover:text-foreground transition-colors duration-200 text-sm cursor-pointer focus-visible:outline-none focus-visible:ring-2 focus-visible:ring-ring rounded-sm" href="/alternative/chatgpt-desktop">ChatGPT desktop alternative</a></li><li><a class="text-muted-foreground hover:text-foreground transition-colors duration-200 text-sm cursor-pointer focus-visible:outline-none focus-visible:ring-2 focus-visible:ring-ring rounded-sm" href="/compare/copilot-for-mac">Echoo vs Microsoft Copilot</a></li><li><a class="text-muted-foreground hover:text-foreground transition-colors duration-200 text-sm cursor-pointer focus-visible:outline-none focus-visible:ring-2 focus-visible:ring-ring rounded-sm" href="/compare/grammarly">Echoo vs Grammarly</a></li></ul></div></div></nav><div class="mt-12 flex flex-col items-center justify-between gap-4 border-t border-border pt-8 sm:flex-row"><div class="flex flex-col items-center gap-3 text-sm text-muted-foreground sm:items-start"><p>© 2026 Echoo. Built for Mac workflows. Third-party trademarks belong to their owners.</p><button type="button" class="rounded-sm underline underline-offset-4 hover:text-foreground focus-visible:outline-none focus-visible:ring-2 focus-visible:ring-ring">Cookie preferences</button></div><p class="flex items-center gap-1.5 text-sm text-muted-foreground">Built for speed<svg xmlns="http://www.w3.org/2000/svg" width="24" height="24" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2" stroke-linecap="round" stroke-linejoin="round" class="lucide lucide-zap h-3.5 w-3.5 fill-warning text-warning" aria-hidden="true"><path d="M4 14a1 1 0 0 1-.78-1.63l9.9-10.2a.5.5 0 0 1 .86.46l-1.92 6.02A1 1 0 0 0 13 10h7a1 1 0 0 1 .78 1.63l-9.9 10.2a.5.5 0 0 1-.86-.46l1.92-6.02A1 1 0 0 0 11 14z"></path></svg></p></div></div></footer></div></div>
87    <!--email_on-->
88  
89
90</body></html>

Line numbers count LF bytes from the start of the resource, as the search results do. Vendor segments are library code the classifier recognised; they are stored but not indexed. Bytes are shown as Latin1 characters, one per byte.