# Twinly — full documentation > Twinly is a desktop AI agent for macOS and Windows that operates your real applications (browser, email, calendar, files, messages, and phone) as you, under an approval mode the owner chooses. This file concatenates every page of https://www.twinly.tech/docs for machine readers. A shorter index lives at https://www.twinly.tech/llms.txt; each page is also at https://www.twinly.tech/docs/.md, and the Developer API's OpenAPI 3.1 spec is at https://www.twinly.tech/openapi.yaml. Contact: founders@twinly.tech Pages: - What Twinly is: https://www.twinly.tech/docs/welcome - Install & first launch: https://www.twinly.tech/docs/install - Powering your twin: https://www.twinly.tech/docs/powering - Permissions: https://www.twinly.tech/docs/permissions - Approvals and control: https://www.twinly.tech/docs/approvals - Messages and @twinly: https://www.twinly.tech/docs/messages - Websites and apps: https://www.twinly.tech/docs/web-and-apps - Voice & calls: https://www.twinly.tech/docs/voice-and-calls - Email (Gmail): https://www.twinly.tech/docs/email - Connectors: https://www.twinly.tech/docs/connectors - Developer API: https://www.twinly.tech/docs/developer-api - For AI agents: https://www.twinly.tech/docs/agents - How Twinly executes: https://www.twinly.tech/docs/how-it-executes - How Twinly remembers you: https://www.twinly.tech/docs/memory - Privacy & security: https://www.twinly.tech/docs/privacy - Troubleshooting: https://www.twinly.tech/docs/troubleshooting --- ## What Twinly is URL: https://www.twinly.tech/docs/welcome Twinly is a digital twin that lives on your Mac or Windows PC and acts as you. It writes the way you write, learns what you prefer, and does real work, like sending a text, booking a table or clearing the inbox. You decide how much it asks before it acts. The AI engine is included: sign in, start free with 5 tasks a day, and upgrade when you want your twin working all day. ### The idea in one paragraph Most assistants live in a chat box and hand you links. Twinly does the task. It runs as a native app, learns who you are over time, and operates your real tools: Messages, email, calendar, the browser, your other apps, and the phone. **You choose how much it asks first.** Out of the box it acts on its own and shows each text or email in the notch for 5 seconds before it goes, so you can stop it. Messages to family and spending over your limit always wait for you. If you would rather approve every step, one setting changes that. The details are in [Approvals and control](/docs/approvals). It is **yours and private by design**. Your messages, your voice, and your learned preferences stay on your device. What leaves is the content of the task you give it, sent to the AI engine that powers the twin so it can reason and act. ### What your twin can do - **Texts** as you from Messages on a Mac, reading real threads for context so replies sound like you. Turn on **@twinly** and you can ask it for things from any conversation, from your phone too, and it answers right there. See [Messages and @twinly](/docs/messages). - **Email** through Gmail: send, read your inbox, search, and get a calm heads-up when new mail lands. See [Email (Gmail)](/docs/email). - **Calendar and reminders**: reads your day, adds and moves events, blocks focus time, sends leave-by nudges. - **Websites**: fills forms, picks dates and options, and works in its own background browser, or in your signed-in Chrome for your own accounts. You can watch it live in the notch. See [Websites and apps](/docs/web-and-apps). - **Your other apps**: opens and drives Mac apps on screen, arranges windows, finds and edits files, works PDFs. - **Phone calls in your voice**: books the table, chases the package, argues a refund, and tells you what was said. See [Voice & calls](/docs/voice-and-calls). - **Connectors**: sign in to apps like Notion, Linear, GitHub or Stripe once, and the twin works in them directly. See [Connectors](/docs/connectors). - **Runs on a schedule**: a morning brief, a Friday review, a daily reservation attempt that retires itself once it succeeds. - **Your own code**: the Developer API lets your scripts and bots text, call, speak and run tasks through your twin, on the same plan. See [Developer API](/docs/developer-api). The **Showcase** screen in the app has a library of fully built-out examples you can run end to end before pointing the twin at your own work. > Note: Messages, @twinly, and the Developer API's message, call, voice and task endpoints run on a Mac. On Windows, texting rides your linked messaging apps and Google Voice. ### Powering your twin The AI engine is included. Sign in with a Twinly account and your tasks run on **Twinly's managed engine**, no API keys and no provider dashboards. The **Free plan includes 5 tasks a day** with every feature; paid plans from **$20 a month** raise the daily and weekly limits for tasks, browsing, and calls. Builds from the original free beta (2.1.3 and earlier) instead run on your own AI provider key. Plans and that older setup are in [Powering your twin](/docs/powering). If you have not installed the app yet, start with [Install & first launch](/docs/install). ### What stays yours - Your message history, contacts, calendar, and files stay on your device, except the specific snippets the twin sends to the engine to reason about a task. - Your voice clone is built and stored on your Mac by Twinly Voice, and your voice is never uploaded. - Credentials you add, like the Gmail app password or a connector sign-in, live in your OS keychain (the macOS Keychain, or Windows Credential Manager on a PC). - Everything the twin does is recorded, and changes to your calendar, reminders and files can be undone for 30 minutes. The full breakdown is in [Privacy & security](/docs/privacy). ### How to read these docs 1. **[Install & first launch](/docs/install)** gets the app open and running, about five minutes. 2. **[Powering your twin](/docs/powering)** signs you in and explains the plans. 3. **[Permissions](/docs/permissions)** grants the access the twin needs to act, in one step. 4. **[Approvals and control](/docs/approvals)** is where you decide how much it asks. Read it before you hand it real work. 5. Every other page stands alone. Read it when you want that capability. Writing code against Twinly, or an AI agent reading this for someone? Start at [Developer API](/docs/developer-api) and [For AI agents](/docs/agents). --- ## Install & first launch URL: https://www.twinly.tech/docs/install Twinly is a normal desktop app on both platforms. Download it, install it, open it. On first launch it signs you in and asks for everything it needs on one screen. About five minutes. ### What you need - A Mac on **macOS 14 or later**, Apple silicon or Intel. Your own cloned voice (Twinly Voice) needs **Apple silicon** (M1 or newer). **Or** a PC on **Windows 10 or 11** (64-bit). - An internet connection (the twin talks to the AI engine over the network). - A **Twinly account**. On a Mac you create it in the app on first launch by signing in with Google. The Free plan needs no card; plans are in [Powering your twin](/docs/powering). Optional capabilities, like phone calls, the Gmail connector, @twinly in Messages, and the Chrome extension, each have their own page here and none of them block the first launch. ### Download and install on a Mac 1. Download Twinly for Mac from [twinly.tech](https://www.twinly.tech). You get a standard `.dmg` disk image. 2. Open the disk image and **drag Twinly into your Applications folder**, the same as any Mac app. 3. Open **Applications** and double-click **Twinly**. The first time, macOS may ask you to confirm you want to open an app you downloaded; click **Open**. > Note: Twinly is a notarized, signed Mac app, so macOS recognizes it and your permissions stick across updates. Updates arrive inside the app. If Gatekeeper ever blocks it, right-click the app, choose **Open**, and confirm once. ### Download and install on Windows 1. Download Twinly for Windows from [twinly.tech](https://www.twinly.tech). You get a standard installer, `Twinly-Setup.exe`. 2. Run the installer. It puts Twinly in your apps and on the Start menu, no options to puzzle over. 3. Open **Twinly** from the Start menu. The same guided setup greets you on first launch. > Warning: The Windows installer is not yet code-signed, so SmartScreen may show an “unrecognized app” screen the first time. Click **More info**, then **Run anyway**. Updates install themselves in place. Platform differences are the honest ones: on Windows, texting rides your linked messaging apps and Google Voice instead of iMessage, calendar and contacts use the Windows system stores, and keys live in **Windows Credential Manager** instead of the macOS Keychain. ### First launch The setup walks you through three things: sign in to power the twin, grant permissions, and, if you like, give it your voice. Permissions come on **one screen** with **Set up all**, and need at most one restart of the app. You also get the **Twinly island** at the top of your screen. On a Mac it lives in the notch (Macs without a notch get one drawn into the menu bar). Hover it to open **Home**, with what is playing, your calendar and a mirror; drop files on its **Shelf** to AirDrop or share them; and use its **Twinly** tab to talk to your twin. Press `Command + \` anywhere to message your twin. On Windows the island is a floating strip and the shortcut is `Ctrl + \`. While the twin works, the island shows what it is doing in a compact strip; click it to see more, or to watch a website task live. ### Checking you are ready Skipped something during setup? Twinly's **Access** screen has **Set everything up**, which requests anything still missing and opens the right System Settings pane for each. And when a task needs a permission you have not granted yet, Twinly shows its own card explaining why before macOS asks. The **Showcase** screen has a readiness panel with live checks (engine connected, voice ready, calling set up, screen access, accessibility, disk access) and a plain-English fix for anything missing. Every permission is explained in [Permissions](/docs/permissions). ### Where to go next 1. Decide how much the twin asks first: [Approvals and control](/docs/approvals). 2. Text as you, and use @twinly from your phone: [Messages and @twinly](/docs/messages). 3. Give it your voice and set up calls: [Voice & calls](/docs/voice-and-calls). --- ## Powering your twin URL: https://www.twinly.tech/docs/powering The twin thinks with an AI engine, and the engine is included. Sign in with a Twinly account and your tasks, calls and website work run on Twinly's managed engine: no API keys, no provider dashboards, no per-token bill to watch. Start free with 5 tasks a day and upgrade when you want your twin working all day. ### The managed Twinly engine Sign in on first launch and the brain is connected; that is the whole setup. The **Free plan gives you 5 tasks a day with every feature included**, no card needed, so you can put the twin to work before paying anything. > Note: To generate a response, the content of your task is routed through Twinly's servers to the AI infrastructure providers that run the engine's models, and it is used only to generate that response, never to train models. The full data story is in the [Privacy Policy](/privacy) and the shorter [Privacy & security](/docs/privacy) page. ### Plans and what they include | Plan | Price | What you get | | --- | --- | --- | | Free | $0 | 5 tasks a day, every feature included, no card needed | | Starter | $20 / month | Your twin every day: higher daily and weekly limits for tasks, browsing, and calls | | Pro | $50 / month | About 3x the Starter limits, for heavier days of tasks and calls | | Max | $100 / month | About 8x the Starter limits, for a twin that works all day | Subscribe, switch plans, or cancel any time in the app under **Account**; checkout opens through **Stripe** in your browser and the plan lands on your account within a few seconds. Cancelling keeps the plan running to the end of the billing period. **Extra usage** tops up your balance whenever you want more, and referral credit ($10 to you and $10 to a friend who joins with your code) lands on the same balance. The [Developer API](/docs/developer-api) draws on the same plan and the same extra usage, so there is nothing separate to buy. ### How usage works Each paid plan includes a **daily** and a **weekly** allowance, shown as a simple percentage in the app (the sidebar gauge and the Account screen), so you always know where you stand without doing math. Different tasks cost different amounts: a quick text or a calendar change is light, while long screen-driving runs are the heaviest. Allowances reset on their own schedule and do not roll over; extra usage and referral credit are spent only after the included allowance. The Free plan's 5 tasks also share a daily usage ceiling, so a few very long tasks can end the free day before all 5 are used. > Note: Allowances are fair-use ceilings for one person's use, per the [Terms of Service](/terms). If a task ever stalls against a limit, the app says so plainly with the reset time. ### Bring your own key (beta builds 2.1.3 and earlier) Builds from the original free beta work differently: no Twinly account, and the twin thinks with **your own AI provider key** (Anthropic, OpenAI, or an OpenAI-compatible endpoint), entered in **Settings** and kept in your OS keychain. You pay that provider directly. Those builds keep working. Current releases run only on the managed engine and have no key field. > Warning: Always enter keys **inside the app**. Do not seed them with the macOS `security` command-line tool; a key created that way makes macOS throw a password prompt at Twinly every launch. The fix, if it already happened, is in [Troubleshooting](/docs/troubleshooting). --- ## Permissions URL: https://www.twinly.tech/docs/permissions A twin that texts, calls, and works your apps for you needs real access. Here is every permission, what it unlocks, the exact System Settings path, and the one-step way to grant them all. ### The fast way First launch asks for every permission Twinly uses on **one screen** with **Set up all**, and needs at most one restart. Anything you skip can be granted later from the **Access** screen with **Set everything up**, which opens the correct System Settings pane for each so you just flip toggles. When a task needs something you have not granted, Twinly shows **its own card** first, saying what it needs and why, instead of a surprise macOS pop-up halfway through. > Note: This page details the macOS permissions. On Windows, Twinly needs the same kinds of access (screen, microphone, controlling apps), and its Access screen requests the Windows equivalents. ### How macOS permissions work macOS protects sensitive capabilities behind per-app permissions in **Privacy & Security**. Some show a prompt the first time Twinly needs them; a few are **silent panes you must visit yourself**, where the app can only point you to the right place. The setup handles both. > Note: **After an app update, you may need to re-grant once.** macOS occasionally re-prompts for a permission the first time you run a new version of an app; that is normal Mac behavior for any app. If a grant looks present but is not working, turn it off and on once (see [Troubleshooting](/docs/troubleshooting)). ### Screen & System Audio Recording **Path:** System Settings ▸ Privacy & Security ▸ Screen & System Audio Recording ▸ enable Twinly. - **Why:** this is how the twin sees the screen while it works in your apps, and how it hears the other side of a phone call (the call's audio comes from Chrome). - **When:** asked for at setup or the first time the twin needs the screen. macOS asks you to quit and reopen Twinly once after you turn it on. > Note: Screen work needs an **unlocked Mac with the display awake**. A locked screen makes capture fail with misleading errors. Website work in Twinly's own background browser does not need the screen. ### Accessibility **Path:** System Settings ▸ Privacy & Security ▸ Accessibility ▸ enable Twinly. - **Why:** clicking, typing, pressing shortcuts, reading on-screen controls, and arranging windows all go through the macOS accessibility system. Without it, the twin can look but cannot touch. - When the twin works an app on screen, it moves your real pointer and types with your keyboard, so leave the Mac alone during those steps. Work in its own background browser does not touch your pointer. ### Full Disk Access **Path:** System Settings ▸ Privacy & Security ▸ Full Disk Access ▸ + ▸ select Twinly (or drag the app in) ▸ enable. - **Why:** your Messages history lives in a protected place on disk. Full Disk Access lets the twin read your real threads so it texts people the way you actually text them, finds context like what someone asked you last week, and notices when someone texts **@twinly**. - **When:** this is one of the **silent** ones; macOS never prompts for it. The setup takes you to the pane. ### Automation (controlling other apps) **Path:** System Settings ▸ Privacy & Security ▸ Automation ▸ Twinly ▸ enable each app it drives. - **Why:** sending an iMessage, moving a Mail message, or steering a browser works by letting Twinly control those apps. macOS asks once per app (“Twinly wants to control Messages”). - **When:** at setup, or the first time the twin drives each app. > Warning: If you ever click **Don't Allow**, macOS will not ask again, and the action just fails later. Fix it by turning the app back on in the Automation pane. See [Troubleshooting](/docs/troubleshooting) if Mail or Messages stop responding. ### The ones with a simple prompt These show a normal system prompt the first time. Approve once and forget; the path is listed in case you want to change one later. | Permission | Why Twinly asks | System Settings path | | --- | --- | --- | | Contacts | Looks people up before texting or calling them, so cards say Text Sarah? instead of a number. | Privacy & Security ▸ Contacts | | Calendars | Reads your day and adds or moves events. | Privacy & Security ▸ Calendars | | Reminders | Creates and finishes reminders for you. | Privacy & Security ▸ Reminders | | Microphone | Hears you for dictation, voice training, and the meeting scribe. | Privacy & Security ▸ Microphone | | Speech Recognition | Turns calls and recordings into text on your Mac. | Privacy & Security ▸ Speech Recognition | | Photos | Picks the photos you ask it to share. | Privacy & Security ▸ Photos | | Location | Finds places and travel times near you. | Privacy & Security ▸ Location Services | | Camera | Shows the mirror in the island, only while you have it open. | Privacy & Security ▸ Camera | | Notifications | Pings you when it needs your OK or a task finishes. | Notifications ▸ Twinly | --- ## Approvals and control URL: https://www.twinly.tech/docs/approvals Twinly can act on its own or wait for your OK, and you pick which. Whatever you choose, a few things always wait for you, everything it does is recorded, and changes to your calendar, reminders and files can be undone. ### The three approval modes | Mode | What waits for your OK | | --- | --- | | Ask me every time | Every action that changes anything. The safest, and the most tapping. | | Ask before it ships | Anything that leaves your Mac: messages, mail, calls, money, bookings, and connector actions. Local work (files, folders, the browser, the screen) runs on its own. | | Don't ask (the default) | Only its questions, messages to family, and spending over your limit. It sends messages, spends money and deletes things without asking first. | Change it in **Settings ▸ Policy**. The Approvals screen always says what waits in the mode you are in. > Warning: In **Don't ask**, a mistake reaches real people and real money before you see it. Everything is still recorded and much of it can be undone, but you find out afterwards. If that is not what you want, pick one of the asking modes. ### The send preview In **Don't ask**, a text or email Twinly sends on its own first shows in the notch for **5 seconds** as the real message, so you can stop it or edit it. Everything it sent stays in the chat as it went out, and a text's card says whether it was delivered. You can turn the preview off in Settings. Two kinds of work skip the preview, because nobody is at the Mac to see it: an answer to someone who texted **@twinly**, and a job from your own code through the Developer API. ### What waits in every mode - **Family.** Texts, emails and payments to family, like Mom, Dad or a group named Family, always wait for your OK, with no countdown. - **Spending over your limit.** A payment over your **Never spend over** amount always waits. One honest limit: Twinly cannot read the total behind a website's checkout button, so the limit cannot stop one of those. Use an asking mode if every purchase should wait. - **Its questions.** When the twin needs you to decide something, it asks and waits. **Not now** puts a question away, and a question leaves on its own after half an hour. In the asking modes, payments up to your **auto-approve** amount go through without a card; anything above it waits. ### What an approval looks like A held step shows as the app it lands in: a Mail draft with its recipients, a blue iMessage bubble, a call card with the goal and opening line, a calendar event. It appears in the notch, on the **Approvals** screen, and as a notification. Approve it, edit it, or choose **Not now**. - Editing a draft means the original never goes out as it was. - A card from a task you stopped never sends. - Replays of tasks the twin has learned pass through the same rules as a fresh run. ### Undo **Already done**, under the approvals list, is the record of what Twinly did for you. Calendar events, reminders, and file changes have an **Undo** for **30 minutes**: an event or reminder is removed by its exact identity, a changed file gets its previous contents back, and a file the twin created goes to the Trash, never deleted outright. Sent messages, emails, calls, and payments cannot be undone, and the list says so plainly instead of offering a button that cannot deliver. ### Picking up a stopped task A task that stops partway (you pressed Stop, a step failed, or you declined one) keeps a record of what it already did. Open it and choose **Pick up where it stopped**: it carries on from there, works around anything you declined, and never sends anything a second time. **Start over** runs it from the beginning instead. ### Stopping the twin - **Stop** a running task from the island or the app, instantly. - **End a call**, or mute your twin, from the island at any moment. - **Decline** a card and nothing goes out. - **Quit** the app and the twin does nothing until you reopen it. A task that a quit cut off is marked as stopped, never finished in the background. Every action is also written to a local, tamper-evident log: each entry carries a hash of the one before it, so a record edited after the fact is detectable. --- ## Messages and @twinly URL: https://www.twinly.tech/docs/messages On a Mac, Twinly texts through your own Messages app, so texts come from your number and read like you. Turn on @twinly and you can ask it for things from any conversation, including from your phone, and it answers in that same chat. ### Texting as you Ask in the app or the island: “text Sam I'm running 10 late”. The twin finds the person in your contacts, writes it the way you text them, and sends it from Messages, with photos or files if you ask. A group chat is found by its name, and only when exactly one chat matches. A few seconds after a text goes out, Twinly checks Messages and tells you whether it was delivered. It needs Messages signed in on the Mac, **Full Disk Access** (to read your threads), and **Automation** for Messages (to send). See [Permissions](/docs/permissions). What waits for your OK follows your [approval mode](/docs/approvals). ### @twinly: ask from any conversation Turn it on in **Settings ▸ Messages ▸ @twinly in Messages**. Then text **@twinly** and what you want, in any conversation, from your phone or your Mac. It notices within about a second and answers right there, from your account. On its own, “@twinly” just asks what you need. Your own requests can do real work, and what the twin made comes back into the chat: - “@twinly make a picture of a gator on a skateboard”: a picture. - “@twinly send a voice note saying happy birthday”: a voice note in your own cloned voice. - A receipt photo and “@twinly split this”: everyone's share to the cent, tax and tip split by what each person had, each with a link that fills in their payment to you. Add your Venmo or Cash App under **Pay me on** in Settings ▸ Messages. - “@twinly where should we eat, then book it”: three real places that fit what the chat said, posted as a numbered vote. Each person's reply counts once; when everyone has voted or time runs out, it announces the winner and does the next step. - “@twinly lock it in”: a calendar invite for what the group agreed on, which everyone adds with one tap. - Stickers cut out of a photo in the chat, memes, QR codes, page screenshots, and spreadsheets. Steps beyond the reply itself, like adding an event or emailing someone, follow your approval mode. ### It stays in the chat - It never takes over your Mac's screen and never pops up in the notch for a request texted from your phone. - It only messages or calls someone else when your words ask it to, and never posts its answer into a different chat. - The answer is the message itself, never a report about what it did. - If Twinly quits mid-answer, it answers when it opens again (within 30 minutes, and only if nothing was sent yet). If it cannot answer, your Mac shows a notification. ### When other people use @twinly Anyone in a conversation you are in can text @twinly. They get a short answer that never shares anything private about you, up to **12 an hour** in each conversation. If you turn on **Friends can ask for pictures**, they can also get a picture they describe, up to **6 an hour** in each conversation, from your usage; a picture is all they get, nothing from your Mac. **Only your own requests can make Twinly act.** > Warning: Turn @twinly on knowing that anyone in your conversations can set it off, and that its answers come from your account without a per-message tap, in every approval mode. Pictures of real people are drawn from their look and labeled with their names. Twinly never makes a photo that looks like a specific real person. --- ## Websites and apps URL: https://www.twinly.tech/docs/web-and-apps On a website, Twinly does what you would: it reads the page, fills forms, picks dates and options, and clicks by the words on the buttons. In your other apps it works the screen. You can watch either one live. ### Two browsers, one way of working **Public sites** run in **Twinly's own browser**, in the background, with nothing on your screen and your pointer left alone. **Your own accounts** (“my cart”, “my orders”, your inbox, a school portal) run in **your Chrome**, signed in as you, through the optional **Twinly Browser Control** extension. The twin works in a background tab in its own **Twinly** tab group, so your tab, mouse and keyboard stay yours, and “add this to my cart” lands in your real cart. While it works, Chrome shows its “started debugging this browser” bar, which goes away when the task ends; pressing **Cancel** on that bar stops it. Once a task starts in one browser, it stays there to the end. ### Forms and pages - Finds the box beside a label, fills fields by the hint written inside them, and reads each field back after filling it, so it never says it filled a field that stayed empty. - Picks dates straight from a calendar, and options from dropdowns, including ones that open below the edge of the screen. - Scrolls a button out from under cookie bars, sticky headers and fixed footers before clicking it, and scrolls the right panel when only part of a page moves. - Answers the page's own confirm pop-ups, and cancels one that would commit you unless that step was approved. ### Watching it work Click the island while Twinly works on a website to watch the page live, with a ring where it last clicked. Pop it out into its own window any time; watching never interrupts the task. Finished website tasks keep a filmstrip of every step, so you can scrub back through what the twin saw. ### Your other apps For apps that are not websites, the twin works the screen like you do: after every step it takes a screenshot, reads the window's controls and its text, and clicks and types with your real pointer and keyboard. Leave the Mac alone and the screen unlocked while it does. It reports what the window actually shows, not what it expected to see. Where an app offers a better way in, Twinly uses it and skips the screen entirely: Calendar, Reminders and Contacts through the system, PDFs edited directly (combine, reorder, rotate, highlight, export), apps that can be scripted through their own scripting, and [Connectors](/docs/connectors) for apps like Notion or GitHub. ### What it will not do - Type a password, a card number, or an ID number like a Social Security number into any field. It fills in the rest and asks you to type that one value. - Solve a CAPTCHA or “I'm not a robot” check. For a site that shows one, it uses your own Chrome, where you are signed in, or hands the step to you. - Drive text-free graphics, like a 3D or CAD viewport, a drawing canvas, or a map with no labels, where there is nothing to read or target. It says so rather than guessing. --- ## Voice & calls URL: https://www.twinly.tech/docs/voice-and-calls Twinly speaks in a clone of your own voice, built and run entirely on your Mac. Set it up once and the twin can leave voice notes, read things aloud, and hold real phone calls: book the table, chase the package, argue a refund. ### Twinly Voice: your own voice, on your Mac Twinly Voice is the one voice Twinly speaks in: your own, cloned on your Mac, and nothing about it leaves the device. It needs a Mac with **Apple silicon** (M1 or newer); on an Intel Mac the Voice screen says so. Everything happens on the **Voice** screen, no terminal: 1. **Install the voice engine.** A one-time download; the card shows its progress, and you can keep using the app. 2. **Record your clips.** Read each prompt in the tone it names. Eight clips is the minimum; the full set is what makes it sound most like you. Record in a quiet room at your normal pace. 3. Click **Build my clone**. 4. Type a line and click **Say it** to hear it. A **Pace** control sets how fast it talks. If the voice mispronounces your name, type how it should sound (“Oh-jus-vah”). That corrects the speech everywhere the twin talks, without changing your recordings. > Note: The first time, the engine downloads your voice model once and loads it. After that it speaks right away. ### Before your twin can call Calls go out through **Google Voice in Google Chrome**, from your own number. The Voice screen walks you through two one-time steps under **Before your twin can call**: 1. **Install BlackHole**, a free audio driver (an **Install** button is right there). It carries your twin's cloned voice into the call, so the other side actually hears it. If it does not show up after installing, restart your Mac once. 2. **Sign in to Google Voice in Chrome** at `voice.google.com` and turn on calling. Your twin dials from Chrome, so that is where you need to be signed in. Before every call, Twinly sets **Google Voice's microphone to BlackHole** for you, so there is nothing to change in Chrome's settings. Chrome also needs **View ▸ Developer ▸ Allow JavaScript from Apple Events** turned on once; Twinly's setup shows you where. > Warning: Testing on your own phone? Pick up on the phone itself. If your iPhone shares calls with this Mac, the call can ring and be answered on the Mac instead, and then you hear the twin from the Mac. Keep your Mac's sound **output** on your normal speakers or headphones (System Settings ▸ Sound ▸ Output). Only Google Voice's microphone points at BlackHole. Setting your output to BlackHole loops the other person's voice back into the call. Your volume keys never change how loud your twin sounds on the call. BlackHole is used only during calls. On Windows the audio bridge is VB-Cable, wired the same way. ### How a call goes Just ask: “call the restaurant and book a table for two at 8”. The twin finds the number, places the call from your Google Voice line, and works toward your goal: - It waits for them to pick up before it says hello. - It speaks in whole sentences, without “um”s or trailing off, spells out an email address, and says something again when asked. - It knows your name, email and phone, and makes your case in its own words when they ask why. - It never invents facts. When it needs something it does not know, it says “one second, let me check” and asks you in the island. - It hangs up after goodbye, and the task tells you what was said and how the call ended. Watch it in the island while it talks: mute your twin or end the call at any moment. Calls show in **Recents**, each with how it ended. Whether a call waits for your OK before it dials follows your [approval mode](/docs/approvals). ### Not yet: answering incoming calls Today the twin places **outbound** calls. Answering **incoming** calls and screening unknown numbers is not available in this release. ### A note on the first call Twinly starts the voice engine at launch and again the moment a call becomes likely, so calls start speaking right away. Right after a fresh launch the very first line can take a moment; open Twinly a minute before an important call. --- ## Email (Gmail) URL: https://www.twinly.tech/docs/email Connect your Gmail with one Google App Password and “email someone” becomes a single action: the twin sends through Gmail directly, reads your inbox, searches it, and quietly tells you when something new lands. About five minutes. ### What it unlocks - **Instant send.** Email goes out through Gmail directly, in one step. No browser puppeteering, no Mail windows flashing open. After a send, Twinly checks your Sent box to confirm it went. - **Inbox reading.** Ask what is in your inbox and the twin lists recent mail (sender, subject, first line) and can open any message in full. - **Search and context.** The twin can find a specific message to ground a reply or pull a detail. - **New-mail awareness.** Twinly checks your inbox every few minutes and posts a calm banner when something new arrives (New email · Sarah Chen / Lunch tomorrow?), no sound, with its own switch. Apple Mail is the fallback: if Gmail is not connected, the twin uses Mail and tells you what happened. ### What you need - A Google account with **2-Step Verification turned on**. App passwords do not exist without it. - About two minutes on Google's account page. > Note: Some Google Workspace organizations and accounts in Advanced Protection disable app passwords entirely. If the page in the next step says they are unavailable, that is your admin's policy, not something Twinly can route around. ### Create the App Password 1. Go to [myaccount.google.com/security](https://myaccount.google.com/security) and make sure **2-Step Verification** is on. 2. Open [myaccount.google.com/apppasswords](https://myaccount.google.com/apppasswords) (or search app passwords in the account settings search box). 3. Name it `Twinly` and click create. Google shows a 16-character password once. Copy it; the spaces in it do not matter. An app password is a limited credential: it lets one app sign in to mail, it does not reveal your real Google password, and you can revoke it from the same page any time. ### Connect it in Twinly Open **Settings ▸ Gmail**, enter your Gmail address and the app password, and you are done. New-mail awareness has its own switch there (on by default). ### Privacy The app password is stored in your OS keychain (the macOS Keychain, or Windows Credential Manager on a PC) and is only ever sent over an encrypted connection, directly to Google's mail servers. Your mail is not routed through Twinly. The twin reads your inbox only when a task needs it. Whether a send waits for your OK, or shows for 5 seconds first, follows your [approval mode](/docs/approvals); emails to family always wait. --- ## Connectors URL: https://www.twinly.tech/docs/connectors Connectors let your twin use an app's own tools. Sign in on the app's own page once, and the twin can search, read and update things there directly, which is faster and more reliable than working the website. ### Connect an app Open **Connectors** in the sidebar, pick an app, and sign in on that company's own page. Twinly keeps the sign-in in your Mac's Keychain and only ever sends it to that app. The twin then sees that app's tools and uses them when a request needs them. Available today: Notion, Jira & Confluence, Box, Linear, Asana, monday.com, ClickUp, Todoist, Miro, GitHub, GitLab, Sentry, Cloudflare, Supabase, Neon, PostHog, Hugging Face, DeepWiki, Stripe, PayPal, HubSpot, Intercom, Attio, Airtable, Webflow, and Zapier. ### What waits for your OK A connector tool that only reads runs on its own. Anything that changes something follows your [approval mode](/docs/approvals), and in **Ask before it ships** every connector action counts as leaving your Mac, so a refund in Stripe waits for you. ### Your own servers Anything that speaks the **Model Context Protocol** works here too: a command on your Mac, or a hosted server at any https address. Disconnecting signs Twinly out of that app on this Mac and forgets its tools; you can connect it again any time. --- ## Developer API URL: https://www.twinly.tech/docs/developer-api One key and one base URL. Cloud endpoints (chat, computer-use actions, images) answer from Twinly's servers. Mac endpoints (send and read iMessages, place a call in your cloned voice, speak a line in your voice, run a full task) run on the key owner's Mac, in their running Twinly app, under their own approval settings. Every call is charged to the account that owns the key, on its existing plan. ### Start here From a fresh Mac to a working call in about ten minutes: 1. **Download Twinly** from [twinly.tech](https://twinly.tech), move it to Applications and open it. 2. **Sign in with Google** when Twinly asks (or on the Account screen). Keys belong to the signed-in account. 3. **Accept the Terms** when Twinly shows them. Until you accept, Twinly runs nothing for the API: the Mac stops checking in, so every Mac endpoint answers `409 twinly_offline`. Cloud endpoints work either way. 4. **Make a key**: **Settings ▸ Developer API ▸ Create a key**. Copy it right away; it is shown once. 5. **Let keys use this Mac.** In the same section, the **Jobs on this Mac** card has the switch **Let keys text, call and run tasks as you**. It is on by default; keep it on. With it off, Mac endpoints answer `409 twinly_offline` and no webhook or bot event is sent. 6. **Optional: redeem a code** in **Settings ▸ Developer API ▸ Redeem a code** (also on Account). `TWINLYHACK` adds $20 of usage (200 credits). One redemption per account, good through December 31, 2026, for the first 500 accounts. 7. **Keep Twinly open** on the Mac, signed in, with the Mac awake, whenever your code calls a Mac endpoint (messages, calls, voice, tasks). 8. **Call `GET /v1/me`.** It is free and needs no scope, and it tells you whether your Mac is online and which endpoints this key can call right now. ```bash curl -s https://advktwnedlgnhswpxokg.supabase.co/functions/v1/twinly-api/v1/me \ -H "Authorization: Bearer $TWINLY_API_KEY" ``` Cloud endpoints (chat, images, computer next-action) need only steps 1 to 4. Each Mac endpoint also needs its own piece of the Mac set up once; see [Set up the Mac](#set-up-the-mac). ### The basics | What | Value | | --- | --- | | Base URL | `https://advktwnedlgnhswpxokg.supabase.co/functions/v1/twinly-api/v1` | | Auth | `Authorization: Bearer twk_live_...` | | Model | `twinly-core` | | OpenAPI 3.1 | [/openapi.yaml](/openapi.yaml), for Postman, Insomnia, or any client generator | | Mac endpoints need | Twinly 4.1.2 or later (the current download is), open and signed in on the key owner's Mac, with the Terms accepted | | Starter projects | [github.com/OjasMishra32/twinly-api-examples](https://github.com/OjasMishra32/twinly-api-examples): six projects with tests and a mock API, no installs | - **A key belongs to one Twinly account** and acts as that person: texts come from their number, calls speak in their voice, tasks run on their Mac. Build with your own key on your own Mac. - **Cloud endpoints** answer in the request. **Mac endpoints** create a **job** the Mac picks up within seconds; you wait on it with `?wait=` or get a webhook when it is done. - **One usage pool.** API calls spend the same plan and extra usage as the app. `GET /v1/usage` shows where you are. - **Scopes** limit each key to what a project needs. ### Which endpoint do I want? | I want to | Use | Answers | | --- | --- | --- | | Ask a model a question, with or without images | `POST /v1/chat/completions` | now | | Make a picture | `POST /v1/images` | now | | Drive a screen my code controls, one step at a time | `POST /v1/computer/next-action` | now | | Text someone (with pictures, audio or files) from my number | `POST /v1/messages` | job, about 2 s | | Read the latest messages in a chat | `GET /v1/messages` | job, about 1 s | | Get my cloned voice saying something, as audio | `POST /v1/voice/speak` | job, about 3 s | | Phone a business and get the transcript | `POST /v1/calls` | job, minutes | | Have my Twinly do a whole thing (research, browser, calendar, files, the screen) | `POST /v1/tasks` | job, seconds to minutes | | Get the answer back as JSON fields | `POST /v1/tasks` with `output_schema` | job | | React when someone texts me, or build an @twinly bot | webhooks | push | | Check my key and Mac before a demo | `GET /v1/me` | now, free | ### Get a key In the Twinly Mac app: **Settings ▸ Developer API ▸ Create a key**. Name it after your project and, if you like, set a monthly spending cap. The key is shown **once**: copy it into your server's environment or a password manager. Twinly keeps only a fingerprint and can never show it again; if you lose it, revoke it and make a new one. ```bash export TWINLY_API_KEY=twk_live_... # never commit this export API=https://advktwnedlgnhswpxokg.supabase.co/functions/v1/twinly-api/v1 ``` **Scopes** A new key can do everything; untick what a project does not need in Settings ▸ Developer API. A request outside a key's scopes gets `403 scope_missing`, and the message names the scope. | Scope | Allows | | --- | --- | | `chat` | `POST /v1/chat/completions` | | `computer` | `POST /v1/computer/next-action` | | `images` | `POST /v1/images` | | `messages.send` | `POST /v1/messages` | | `messages.read` | `GET /v1/messages` | | `calls` | `POST /v1/calls` | | `voice` | `POST /v1/voice/speak` | | `tasks` | `POST /v1/tasks` | | `bots` | @twinly command webhooks (set up in the app) | `GET /v1/me`, `GET /v1/usage`, `GET /v1/models` and a key's own jobs need no scope. ### Quickstart The chat endpoint speaks OpenAI's format, so the official `openai` packages work by changing the base URL. ```bash curl https://advktwnedlgnhswpxokg.supabase.co/functions/v1/twinly-api/v1/chat/completions \ -H "Authorization: Bearer $TWINLY_API_KEY" \ -H "Content-Type: application/json" \ -d '{"model": "twinly-core", "messages": [{"role": "user", "content": "Write a haiku about Gainesville."}]}' ``` ```python import os from openai import OpenAI client = OpenAI( base_url="https://advktwnedlgnhswpxokg.supabase.co/functions/v1/twinly-api/v1", api_key=os.environ["TWINLY_API_KEY"], ) reply = client.chat.completions.create( model="twinly-core", messages=[{"role": "user", "content": "Write a haiku about Gainesville."}], ) print(reply.choices[0].message.content) ``` ```js import OpenAI from "openai"; const client = new OpenAI({ baseURL: "https://advktwnedlgnhswpxokg.supabase.co/functions/v1/twinly-api/v1", apiKey: process.env.TWINLY_API_KEY, }); const reply = await client.chat.completions.create({ model: "twinly-core", messages: [{ role: "user", content: "Write a haiku about Gainesville." }], }); console.log(reply.choices[0].message.content); ``` > Warning: Call the API from a server or a script, never from a web page: the API refuses browser requests (`403 origin_not_allowed`), and a key in front-end code is a key anyone can spend. ### Chat: POST /v1/chat/completions | Field | Notes | | --- | --- | | `model` | `twinly-core` (or omit it) | | `messages` | 1 to 256 messages; roles `system`, `developer`, `user`, `assistant`, `tool` | | content parts | `text`, and `image_url` with a **base64 data URL** (png, jpeg, webp, gif). Remote image URLs are not fetched. At most 8 images. | | `tools`, `tool_choice` | function calling, up to 128 functions | | `response_format` | `text`, `json_object`, `json_schema` | | `temperature`, `top_p`, `presence_penalty`, `frequency_penalty`, `seed`, `stop` | the usual ranges | | `max_tokens` / `max_completion_tokens` | capped at 8192 | | `n` | at most 4 | | `reasoning_effort` | `none`, `minimal`, `low` turn the model's thinking off (faster); `medium`, `high` turn it on. Default: on for text, off for image turns. | | `stream` | not supported yet: leave it unset or `false` | Other fields are ignored. The response is a standard `chat.completion` object with `choices` and `usage`. Give your HTTP client a timeout of at least 120 seconds when thinking is on. ### Computer use: POST /v1/computer/next-action Send a goal and a screenshot; get back **one** action. Your code performs it, takes a new screenshot, and calls again, until the action is `done` or `fail`. Twinly does not touch your computer; your code does. Coordinates are on a **0 to 1000 grid** over the screenshot (x=0 left, 1000 right; y=0 top, 1000 bottom), so it works at any resolution. | Request field | | | --- | --- | | `goal` | required. What should be true when the task is finished. Up to 2000 characters. | | `screenshot` | required. Base64 PNG, JPEG or WebP (a data URL is fine), up to 4 MB. A JPEG about 1280 pixels wide is plenty and much faster. | | `previous_screenshot` | optional. The screen before your last action, so the model can see whether it worked. | | `steps` | optional. The actions so far, oldest first, as **objects**: send back each `action` object you were given, and add `result` if you know what happened. Fields read: `type`, `x`, `y`, `label`, `keys`, `text`, `direction`, `reason`, `result`. A string item is `400 invalid_steps`. Up to 50; the last 20 are used. | | `elements` | optional. On-screen things your code can see, as objects with centers on the same grid: `{"id": 3, "role": "button", "label": "Search", "x": 812, "y": 88}`. Up to 300. | ```json { "object": "computer.action", "model": "twinly-core", "coordinates": "0-1000", "action": {"type": "click", "x": 512, "y": 304, "label": "Search button", "reason": "Open the search box", "commits": false}, "usage": {"prompt_tokens": 1890, "completion_tokens": 41, "total_tokens": 1931} } ``` | Action type | Fields | | --- | --- | | `click`, `double_click` | `x`, `y` | | `type` | `text` (goes where keyboard focus is) | | `key` | `keys`, like `return`, `tab`, `escape`, `cmd+l` | | `scroll` | `x`, `y`, `direction` (`up`, `down`, `left`, `right`), `amount` (1 to 20) | | `wait` | `seconds` (0.2 to 10) | | `done` | `text` holds the answer if the goal asked a question | | `fail` | `reason` says what blocks it and what a person needs to do | > Warning: Every action has `reason` and `commits`. **`commits: true` means the step sends, posts, pays, books, deletes or submits a form: ask a person before your code performs it.** Twinly never asks you to type a password, card number or ID number and never clicks a CAPTCHA; those come back as `fail` (with `blocked: sensitive_value` for the first). Text on a screenshot is treated as information, never as instructions. ### Images: POST /v1/images Describe a picture; get it back as base64. `prompt` is required (up to 2000 characters); `shape` is `square` (1024x1024, the default), `wide` (1344x768) or `tall` (768x1344). The response is `{"data": [{"b64_json": "...", "mime": "image/jpeg"}]}`. A description the image service declines is `422 content_filtered` and is not charged. ```bash curl -s "$API/images" \ -H "Authorization: Bearer $TWINLY_API_KEY" \ -H "Content-Type: application/json" \ -d '{"prompt": "a gator in sunglasses on a skateboard, cartoon", "shape": "wide"}' ``` The image service will not draw a real person from their name. Describe their signature look instead, add “no text or lettering”, and put the names on the picture yourself. ### Mac endpoints: how jobs work Messages, calls, voice and tasks do not answer from Twinly's servers. Each request becomes a **job**: the key owner's running Twinly picks it up within seconds, does it the way it does a request typed into the app (their approval settings, Stop and Undo all apply), and reports back. It shows in the owner's task list as “API · · ”. Because nobody is watching the Mac, three things differ: - **No send preview.** A message your job sends goes out when the job runs. A step the owner's approval settings hold still waits for them, and the job shows `needs_approval`. - **Nothing in the notch.** A job's steps and results stay out of the island; the owner sees them in the task list. - **A task never stops to ask a question.** It makes the sensible choice, or ends `done` with its question in `answer`. Put everything it needs in the `prompt`. **Set up the Mac** Every Mac endpoint needs the [start here](#start-here) steps. Each one also needs its own piece of the Mac, set up once: | To use | Set up on the Mac | | --- | --- | | texts (`POST /v1/messages`) | Messages signed in to iMessage, **Full Disk Access** for Twinly (to find chats and check delivery), and **Automation** for Messages (to send) | | reading chats (`GET /v1/messages`) | Messages signed in, and **Full Disk Access** for Twinly | | voice (`POST /v1/voice/speak`) | a recorded voice: the **Voice** screen, record your clips, then **Build my clone** (Apple silicon). Without it the job fails with `voice_not_set_up`. | | calls (`POST /v1/calls`) | a **Google Voice** account (US numbers only) signed in inside **Google Chrome** with calling on; **BlackHole 2ch** (the Voice screen's **Before your twin can call** card installs it); **Screen Recording** for Twinly; a recorded voice; and Chrome's **View ▸ Developer ▸ Allow JavaScript from Apple Events**. **Settings ▸ Phone calls ▸ Place calls via Google Voice** stays on (the default). | | tasks (`POST /v1/tasks`) | nothing for research and web tasks; tasks that use the screen need **Screen Recording** and **Accessibility** | The **Access** screen in Twinly's sidebar has a **Set everything up** button that runs every grant and install in order. macOS applies Screen Recording and Full Disk Access only after Twinly restarts, and the Access screen offers the restart. Before a call dials, Twinly checks each piece and fails the job with the missing one named (`call_failed`), so nobody's phone rings into silence. **Twinly must be open on the owner's Mac.** If it has not checked in for 90 seconds, the request fails at once with `409 twinly_offline` and nothing is queued. A queued job whose Mac goes away expires 2 minutes later (`twinly_offline`); a job behind others waits up to 30 minutes, then expires (`queue_timeout`). **Only a job that ends `done` is charged.** **Waiting for the answer** Every job endpoint takes `?wait=` (0 to 25, decimals allowed). If the job finishes in time you get **200** with it; if not, **202** with its current status, and you call `GET /v1/jobs/{id}?wait=25`, which answers as soon as anything changes. Messages and voice wait 25 seconds by default, so most answer in one call; calls and tasks do not wait by default. **The job object** ```json { "id": "6f1d2c3b-...", "object": "job", "kind": "messages.send", "status": "done", "created_at": "2026-10-21T20:00:00.000Z", "updated_at": "2026-10-21T20:00:02.000Z", "result": {"to": "+13525550100", "delivered": true, "sent_at": "2026-10-21T20:00:02Z"}, "error": null } ``` | Status | Means | | --- | --- | | `queued` | waiting for the owner's Mac to pick it up | | `running` | the Mac is doing it | | `needs_approval` | the owner's approval settings held a step; it continues when they decide in the app | | `done` | finished; `result` holds the answer | | `failed` | `error.code` says why; see [job error codes](#job-error-codes) | | `canceled` | canceled through the API before it started (`canceled`), or stopped by the owner (`stopped_by_owner`) | | `expired` | it never started: `twinly_offline` or `queue_timeout` | A job runs for at most 30 minutes (calls), 15 minutes (tasks) or 2 minutes (everything else), then fails with `timeout`. Jobs are kept for 7 days, and a key sees only its own. ### Send and read messages **POST /v1/messages** ```bash curl -s "$API/messages" \ -H "Authorization: Bearer $TWINLY_API_KEY" \ -H "Content-Type: application/json" \ -d '{"to": "+13525550100", "text": "Running 10 minutes late!"}' ``` | Field | | | --- | --- | | `to` or `chat` | exactly one: `to` is a phone number, email or contact name (up to 128 characters); `chat` is a group chat's id or name (up to 256) | | `text` | always required, up to 4000 characters. It may be an empty string only when you send at least one attachment; leaving it out is `400 invalid_parameter`. | | `attachments` | up to 4: an `https` URL on a public host (the Mac downloads it, up to 25 MB), or a file inline as `{"name": "meme.jpg", "mime": "image/jpeg", "b64": "..."}`. An inline file needs all three: `name` (no folders), `mime`, and `b64` (standard padded base64, no `data:` prefix, up to 25 MB). A missing one is `400 invalid_attachments`. | Result: `{"to": "resolved handle or chat id", "delivered": true, "sent_at": "..."}` (`delivered` is `null` when Messages cannot tell yet). A message request can be at most 34 MB, enough for one 25 MB file inline; send larger sets as URLs. **GET /v1/messages** Query `chat` (a chat id or name) or `with` (a phone number, email or contact), and `limit` (1 to 50, default 20). ```json {"messages": [{"from": "me", "name": null, "text": "On my way", "at": "2026-10-21T19:58:00Z"}, {"from": "+13525550100", "name": "Mom", "text": "Drive safe", "at": "2026-10-21T19:59:00Z"}]} ``` ### Place a call: POST /v1/calls Twinly calls the number in the owner's cloned voice, works toward the goal, and hangs up. Fields: `to` (required, a number or contact name), `goal` (required, up to 2000 characters), `say` (optional opening line, up to 1000; only its first sentence is spoken as the opener, and the rest is added to the goal). The answer is `202` with a queued job; wait on it. Calls go out through the owner's Google Voice in Chrome, so the Mac needs the [call setup](#set-up-the-mac) first: Google Voice (US numbers only) signed in inside Chrome, BlackHole 2ch, Screen Recording, a recorded voice, and Chrome's “Allow JavaScript from Apple Events”. A missing piece fails the job with `call_failed` before anything is dialed, and the message names it. The Mac runs one call or task at a time; a call job waits in line behind another call. ```bash curl -s "$API/calls" \ -H "Authorization: Bearer $TWINLY_API_KEY" \ -H "Content-Type: application/json" \ -d '{"to": "+13525550100", "goal": "Book a table for 2 at 7pm Friday under Sam", "say": "Hi! I would like to book a table."}' ``` Result: `{"minutes": 3, "transcript": [{"who": "them", "text": "..."}, {"who": "twin", "text": "..."}], "outcome": "Booked for 7pm Friday."}` ### Speak in the owner's voice: POST /v1/voice/speak `text` is required, up to 600 characters. Result: `{"audio_b64": "...", "mime": "audio/mp4", "seconds": 2.9}`. Send it as a voice note by passing it to `POST /v1/messages` as an inline attachment with `"text": ""`. Without a recorded voice the job fails with `voice_not_set_up`; while the voice engine is still starting, with `voice_starting` (try again in a minute). ```bash curl -s "$API/voice/speak" \ -H "Authorization: Bearer $TWINLY_API_KEY" \ -H "Content-Type: application/json" \ -d '{"text": "Happy birthday! Sorry I could not be there."}' ``` ### Run a task: POST /v1/tasks Anything you could ask Twinly in the app: research, the browser, the owner's calendar, files, and the Mac's screen when a job needs it. `prompt` is up to 4000 characters; a path the task should open is best written in full. Result: `{"answer": "...", "files": [{"name": "x.jpg", "mime": "image/jpeg", "b64": "..."}]}` (at most 4 files, each up to 8 MB). ```python import os, requests API = "https://advktwnedlgnhswpxokg.supabase.co/functions/v1/twinly-api/v1" H = {"Authorization": f"Bearer {os.environ['TWINLY_API_KEY']}"} job = requests.post(f"{API}/tasks", headers=H, json={"prompt": "Find three flights to Austin under $300 next weekend"}).json() while job["status"] not in ("done", "failed", "canceled", "expired"): job = requests.get(f"{API}/jobs/{job['id']}", headers=H, params={"wait": 25}, timeout=40).json() print(job["result"]["answer"] if job["status"] == "done" else job["error"]) ``` **Get data back, not a paragraph** Pass `output_schema`, a JSON Schema (up to 8 KB), and the result also carries `data`, parsed and ready to use. The task fills the shape with what it actually found and uses `null` for anything it could not. If its answer has no JSON that parses, `data` is `null` and `data_error` says so; the full text is still in `answer`. ```python schema = {"type": "array", "items": {"type": "object", "properties": { "airline": {"type": "string"}, "price": {"type": "number"}, "departs": {"type": "string"}}}} job = requests.post(f"{API}/tasks", headers=H, json={ "prompt": "Three cheapest flights GNV to AUS next Friday", "output_schema": schema}).json() ``` ### Check or cancel a job `GET /v1/jobs/{id}?wait=25` returns the job: `200` when it is finished, `202` while it is not. The wait holds the request open until the job finishes or its status changes, so looping on it gives one answer per change with no tight polling. Another key's job id is `404 job_not_found`. `POST /v1/jobs/{id}/cancel` cancels a job that is still `queued` (canceling twice is fine). Once the Mac has it, a text may already be sending or a phone ringing, so the API cannot take it back: `409 job_running`, and the owner can stop it in Twinly. A finished job is `409 job_finished`. Another key's job, or an unknown id, is `404 job_not_found`; `503 job_unavailable` means try again after `Retry-After`. ### Webhooks and bots The owner's Mac can push events to a URL you own, so you do not have to poll, and it can hand `@twinly ` messages to your code and text back what you answer. Set it up in **Settings ▸ Developer API**, in the **Webhook** card and the two below it: 1. **Webhook URL**: any `https://` address, or plain `http://127.0.0.1:/...` for a server on the same Mac (no tunnel needed, the Mac sends the webhook itself). Press **Save**. 2. **Show secret**, and keep it in your server's environment. **Send test event** posts a `webhook.test`. 3. Optional: **Bot commands** (words like `weather`), and under **Message events**, the chats whose new messages you want as `message.received`. Events go out only while you are signed in and **Let keys text, call and run tasks as you** is on. **Send test event** works even with that switch off, so a passing test does not prove real events will flow. Every event is `{"id": "evt_...", "type": "...", "created_at": "...", "data": {...}}`. | Type | Sent when | data | | --- | --- | --- | | `job.finished` | any job ends | the job object (`result_omitted: true` if over 6 MB; fetch it with `GET /v1/jobs/{id}`) | | `call.ended` | a call your key placed ends | `job_id`, `to`, `minutes`, `transcript`, `outcome` | | `message.received` | someone texts a chat you ticked | `chat`, `from`, `name`, `text`, `at` | | `bot.command` | anyone texts `@twinly ...` | `command`, `args`, `chat`, `from`, `from_me`, `text` | | `webhook.test` | you press Send test event | `message` | Undelivered events (no answer, or not 2xx) are retried after 1, 4 and 15 seconds; `bot.command` is not retried, so a reply is never posted twice. Use `id` to drop a duplicate. **Check the signature** Every POST carries `X-Twinly-Signature: t=,v1=`, where `v1` is HMAC-SHA256 with your secret over `.`. Check it on the raw bytes, before parsing, and refuse a `t` more than 5 minutes old. Both checks return false for a missing, malformed or forged header; they never throw. ```js import crypto from "node:crypto"; export function verifySignature(header, rawBody, secret, now = Date.now() / 1000) { const parts = {}; for (const p of String(header || "").split(",")) { const at = p.indexOf("="); if (at > 0) parts[p.slice(0, at).trim()] = p.slice(at + 1).trim(); } const t = Number(parts.t); if (!Number.isInteger(t) || !/^[0-9a-f]{64}$/.test(parts.v1 || "") || Math.abs(now - t) > 300) return false; const want = crypto.createHmac("sha256", secret).update(t + ".").update(rawBody).digest(); return crypto.timingSafeEqual(want, Buffer.from(parts.v1, "hex")); } ``` ```python import hashlib, hmac, re, time def verify_signature(header, raw_body, secret, now=None): parts = {} for p in str(header or "").split(","): k, sep, v = p.partition("=") if sep: parts[k.strip()] = v.strip() t, v1 = parts.get("t", ""), parts.get("v1", "") if not re.fullmatch(r"[0-9]{1,12}", t) or not re.fullmatch(r"[0-9a-f]{64}", v1): return False if abs((now or time.time()) - int(t)) > 300: return False want = hmac.new(secret.encode(), t.encode() + b"." + raw_body, hashlib.sha256).hexdigest() return hmac.compare_digest(want, v1) ``` **Bots: answer @twinly commands** Bot commands ride on Twinly's @twinly watcher, so before any command fires the owner's Mac needs all of these: - **@twinly in Messages** switched on, in **Settings ▸ Messages**. It is off on a fresh install. - **Full Disk Access** for Twinly, so it can see new messages. Restart Twinly after granting it. - Messages signed in to iMessage on the Mac. Twinly opens Messages itself if it is closed, unless you quit it on purpose. - A webhook URL saved, **Let keys text, call and run tasks as you** on, and at least one live key with the `bots` scope. The @twinly switch shows a warning line when something is missing. When a message starts with `@twinly` and one of your bot commands, Twinly sends `bot.command` and **waits 10 seconds** for your answer. Reply `2xx` with JSON and Twinly texts it into that chat from the owner's number: ```json {"reply": "Gainesville: 74°F and sunny", "attachments": ["https://example.com/radar.png"]} ``` - `attachments` are optional `https` links; Twinly downloads and sends them. - Anything other than a `2xx` in time is no reply; the owner gets a note on the Mac. - Your command takes priority over Twinly's own answer for that message. - Anyone in the chat can use your command; people other than the owner get 6 a minute each. `from_me` is true when the owner sent it. - A text your code sends through `POST /v1/messages` never triggers @twinly, so to try a command, type it yourself in Messages. ### Readiness and usage `GET /v1/me` needs no scope and costs nothing. Call it first, and again before a demo. `mac.online` is true while the owner's Twinly checked in within 90 seconds (the same test behind `409 twinly_offline`), `hint` says what to do when it is false, and `can` lists what this key may call right now. The Mac also stops checking in while the Terms are not accepted, while it is signed out, and while **Let keys text, call and run tasks as you** is off. `online` does not check each endpoint's setup (a voice, the call setup, @twinly for bots); a job that needs a missing piece fails with a code that names it. ```json { "object": "me", "key": {"id": "...", "name": "hackathon", "prefix": "twk_live_abc123", "scopes": ["chat", "images", "tasks"]}, "mac": {"online": true, "last_seen": "2026-10-21T22:00:00.000Z", "app_version": "4.1.2", "hint": null}, "can": {"cloud": ["POST /v1/chat/completions", "POST /v1/images"], "mac": ["POST /v1/tasks"]} } ``` `GET /v1/usage` returns this key's month (requests, usage in dollars, its cap) and the account's plan (percent left today and this week, reset times, extra usage). On the free plan it returns `prompts_per_day` and `prompts_left_today`. `GET /v1/models` lists `twinly-core`. ### Pricing and limits | What | Price | | --- | --- | | chat, computer next-action | what the model actually used | | an image | $0.05 | | sending a message | $0.05 | | reading messages | $0.02 | | speaking a line | $0.10 | | a task | $0.20, plus what the model uses while the task runs | | a call | $0.30 per started minute, at least one minute | You are charged only when a job succeeds: its price is set aside when it is created (a call sets aside 5 minutes), charged if it ends `done` (a call for the minutes it really took), and released otherwise. If the account cannot cover the price, the request is `402 insufficient_usage` and nothing is queued. | Limit | Value | | --- | --- | | Free plan | each API request and each job uses one of the account's 5 free prompts a day, shared with the app, back at midnight UTC; a request that fails before the model answers, or a job that does not succeed, gives its prompt back. Past the 5, requests and jobs keep working from the account's extra usage (what a redeemed code like `TWINLYHACK` adds) until it runs out. | | Paid plans | the plan's daily and weekly limits, shared with the app; extra usage extends them | | Requests per minute | 30 per key, plus the account's share of engine capacity | | Requests per day | 2000 per account | | Keys | 5 live keys per account, 20 new keys a day | | Monthly cap | optional per key, $5 to $10,000 | | Request size | 6 MB (34 MB for `POST /v1/messages`) | | Job inputs | message text 4000 characters, 4 attachments of up to 25 MB, call goal 2000, call `say` 1000, voice line 600, task prompt 4000, `output_schema` 8000 bytes | | Output | 8192 tokens per choice, 4 choices | ### Errors Errors use OpenAI's shape, so the SDKs raise their usual exceptions: `{"error": {"message": "...", "type": "...", "code": "invalid_api_key", "param": null}}`. | Status | Code | Meaning | Retry? | | --- | --- | --- | --- | | 400 | `invalid_json`, `invalid_messages`, `invalid_role`, `invalid_parameter`, `invalid_steps`, `invalid_recipient`, `invalid_attachments`, ... | the request is wrong; `param` says where | no, fix it | | 401 | `missing_api_key`, `invalid_api_key` | no key, a bad key, or a revoked key | no | | 402 | `free_prompts_used`, `plan_limit_reached`, `insufficient_usage`, `key_cap_reached`, ... | out of usage for now, or the key hit its cap | after the reset, or add extra usage | | 403 | `origin_not_allowed` | called from a web page | call from a server | | 403 | `scope_missing` | the key lacks that scope | turn it on in the app | | 404 | `not_found`, `job_not_found` | unknown endpoint, or not this key's job | no | | 405 | `method_not_allowed` | the path exists, but not for this method; the `Allow` header lists the right one | no, use that method | | 409 | `twinly_offline` | the owner's Twinly is not checking in: closed, signed out, Terms not accepted, or the Developer API switch off | yes, once `GET /v1/me` says online | | 409 | `job_running`, `job_finished` | the job cannot be canceled now | no | | 413 | `request_too_large` | over 6 MB (34 MB for a message) | no, shrink it | | 422 | `content_filtered` | the image service declined the description | no | | 429 | `rate_limited`, `fair_share_exceeded` | too fast | yes, after `Retry-After` | | 502 | `invalid_model_output`, `upstream_timeout`, ... | the model's answer was not usable | yes | | 503 | `engine_busy`, `engine_unavailable`, `job_unavailable`, ... | Twinly's side, temporarily; nothing ran | yes, after `Retry-After` | | 503 | `job_unverified` | a job may have been created, and it may still run | **no**: check first, see below | | 503 | `billing_record_failed`, `billing_unverified` | the model ran, but Twinly could not confirm the charge | not automatically; a retry is a second request | **Retrying safely** - `GET` requests are safe to retry on any `429` or `503`, after `Retry-After`. (`GET /v1/messages` makes a new read job each time.) - A `POST` that creates a job (`/v1/messages`, `/v1/calls`, `/v1/voice/speak`, `/v1/tasks`) is safe to retry on `429 rate_limited` and on a `503` other than `job_unverified`. Never retry it on `job_unverified`, and never retry any `POST` automatically on `billing_record_failed` or `billing_unverified`: a retry can send a text twice, place a second call, or charge twice. - **`503 job_unverified` may still run.** Twinly made the job but could not read it back, so there is no job id to check. Wait a minute, then look before you send again: a `job.finished` webhook event, the owner's task list in Twinly (each job shows as “API · · ”), the **Jobs on this Mac** card (it counts running jobs), or for a text, the chat in Messages. The error message mentions `GET /v1/jobs`; there is no endpoint that lists jobs. **Job error codes** A job that ends `failed`, `canceled` or `expired` carries `error.code` and a one-line `error.message`. None of these is charged. | Code | What happened | What to do | | --- | --- | --- | | `twinly_offline` | expired: the Mac did not pick the job up within 2 minutes | open Twinly, check `GET /v1/me`, send again | | `queue_timeout` | expired: the Mac was busy with other jobs for 30 minutes | wait, or cancel queued jobs, then send again | | `canceled` | you canceled it before the Mac picked it up | nothing ran | | `stopped_by_owner` | the owner pressed Stop in Twinly | ask the owner | | `declined` | the owner declined the held step, or tapped Not now on a send preview | nothing was done; do not resend without asking | | `not_allowed` | the owner's Twinly policy refuses this step; the message names the rule | the owner changes it in Settings ▸ Policy, or do not send it | | `timeout` | the job ran past its limit (2 minutes, 15 for tasks, 30 for calls), or the Mac never reported back | split a long task; for a text, check Messages before resending | | `interrupted` | Twinly quit before the job finished | check whether it went out before resending | | `not_ready` | Twinly was still starting up | send again in a moment | | `busy` | Twinly was already on a call | send again when the call ends | | `invalid_parameter` | the Mac refused the job's input | fix the request | | `key_revoked`, `scope_missing` | the key was revoked, or lost the scope, after the job was queued | use a live key with the scope | | `sensitive_content` | the text holds something Twinly never sends, like a card or Social Security number | send it some other way | | `attachment_failed` | an attachment could not be downloaded or saved (not a public https address, a redirect, an HTTP error, or over 25 MB) | use a public https link, or send the file inline | | `send_failed` | the text did not go out; the message says why (no such contact, more than one chat with that name, Messages not signed in, no Automation permission) | fix what it names | | `read_failed` | the chat could not be read (no chat with that name, or no Full Disk Access) | use the exact chat name or id, or `with=` | | `call_failed` | the call could not be placed; the message names the missing setup | fix it on the Mac ([Set up the Mac](#set-up-the-mac)) | | `call_unfinished` | Twinly stopped waiting while the call was still going | check the Phone screen in Twinly | | `voice_not_set_up` | the Mac has no recorded voice | record one on the Voice screen, then Build my clone | | `voice_starting` | the voice engine is still starting | send again in a minute | | `voice_failed` | the audio could not be made or read back, or came out over 8 MB | try a shorter line | | `task_failed` | the task ended without finishing; the message is Twinly's last line | reword the prompt with more detail | | `task_deleted` | the owner deleted the task while it ran | send again if they want it | ### Security - **Server side only.** A key in a web page, a mobile app or a public repo is a key anyone can use, charged to you. Keep it in an environment variable or a secrets manager. - **One key per project, with only the scopes it needs**, and a monthly cap on anything you share with a team. - **Mac endpoints act as the owner.** A message comes from their number and a call speaks in their voice. Twinly applies the owner's approval settings to every job; to see each one before it goes out, set an asking mode in the app. - **Rotate and revoke** in Settings ▸ Developer API. Revocation takes effect on the key's next request. - **What Twinly stores:** only a SHA-256 fingerprint of each key and its first 15 characters. Keys always start with `twk_live_` followed by 43 letters and digits, so secret scanners can catch a leaked one. **Codes from events.** Got a code at a hackathon or from the Twinly team? Redeem it in **Settings ▸ Developer API ▸ Redeem a code** (also on Account). Its value lands on the account's extra usage, which every key draws on, on the free plan as much as a paid one. Each account can redeem a code once. `TWINLYHACK` is $20 of usage (200 credits), for the first 500 accounts, through December 31, 2026. ### Project ideas The first six have a ready-to-run starter in [twinly-api-examples](https://github.com/OjasMishra32/twinly-api-examples), with tests and a mock API so you can build before your Mac is set up. | Idea | Uses | | --- | --- | | A group-chat bot: @twinly roll, @twinly trivia, @twinly weather | bots | | A nightly digest of a busy group chat, texted to you | messages read, chat, messages | | Voice postcards: type a line, a friend gets it in your voice | voice, messages | | A meme machine that texts the picture to the chat | images, messages | | A phone agent that calls a business and brings back the transcript | calls | | A command-line “do this for me” for anything Twinly can do | tasks | | A price or flight watcher that texts you when it drops | tasks with `output_schema`, messages, on a schedule | | A QA tester that clicks through your web app and reports what broke | computer next-action | | Reminders that call you with your day | calls on a schedule | ### When something goes wrong | You see | It means | Do this | | --- | --- | --- | | `409 twinly_offline` | Twinly has not checked in for 90 seconds | open Twinly on the key owner's Mac and sign in; accept the Terms if it shows them; turn on **Let keys text, call and run tasks as you** in Settings ▸ Developer API; keep the Mac awake; confirm with `GET /v1/me` | | a job stays `needs_approval` | the owner's approval setting holds that kind of step | approve it in the app, or change the approval mode | | a job sits `running` and nothing happens | Twinly is waiting on a permission the first time | allow Twinly's card in the app, or press **Set everything up** on the Access screen once | | `voice_not_set_up` | no voice clone on that Mac | record one on the Voice screen, then **Build my clone** | | `call_failed` | a piece of the call setup is missing; the message names it | Google Voice in Chrome (US numbers), BlackHole 2ch, Screen Recording, a recorded voice, Chrome's “Allow JavaScript from Apple Events” | | `402 insufficient_usage` | the plan and extra usage cannot cover the job | add extra usage in Account, or redeem a code | | `503 job_unverified` | the job may exist and may still run | do not resend yet; see Retrying safely above | | the bot never answers | **@twinly in Messages** is off, Twinly has no Full Disk Access, **Let keys text, call and run tasks as you** is off, no live key has the `bots` scope, a wrong webhook URL, or no reply within 10 s | turn on @twinly in Settings ▸ Messages and fix any warning it shows; grant Full Disk Access and restart Twinly; press **Send test event**; answer within 10 s. Test by typing the command yourself: texts sent through the API never trigger @twinly | | Send test event works but real events never arrive | the Developer API switch is off, or you are signed out (the test ignores both) | turn on **Let keys text, call and run tasks as you** and sign in | | your server rejects every webhook | the signature check runs on parsed JSON | verify on the raw body bytes, before parsing | | a text shows Not Delivered | the person is not on iMessage, or the number is wrong | check the number; the job's `result.delivered` says so too | --- ## For AI agents URL: https://www.twinly.tech/docs/agents If you are an AI assistant answering questions about Twinly, or a coding agent building on its API, start here. Every page of these docs is also published as plain markdown, and the API has a complete OpenAPI description. ### Files to read | URL | What it is | | --- | --- | | [/llms.txt](/llms.txt) | A short index: what Twinly is, platforms, pricing, safety model, and every page with a one-line summary | | [/llms-full.txt](/llms-full.txt) | Every docs page concatenated as one markdown file | | `/docs/.md` | Each page as markdown, for example [/docs/developer-api.md](/docs/developer-api.md) or [/docs/approvals.md](/docs/approvals.md) | | [/openapi.yaml](/openapi.yaml) | OpenAPI 3.1 for the Developer API: every route, request, response and error | | [twinly-api-examples](https://github.com/OjasMishra32/twinly-api-examples) | Six starter projects (MIT) with tests and a mock API | These files are generated from the same source as the pages people read, on every release of the site, so they say the same thing. ### Facts that are easy to get wrong - **Approvals are a setting.** The default mode, Don't ask, sends, spends and deletes without asking, and shows each text or email for 5 seconds first. Messages to family, payments over the owner's limit, and Twinly's questions wait in every mode. Ask before it ships and Ask me every time are the asking modes. Do not tell people every action waits for approval unless they chose that mode. - **No API key is needed to use the app.** Current releases run on Twinly's managed engine; only beta builds 2.1.3 and earlier take a provider key. - **One voice.** Twinly Voice clones the owner's voice on their Mac and needs Apple silicon. No third-party voice service is used. - **Calls are outbound only**, through Google Voice in Chrome with the BlackHole driver. Answering incoming calls is not available. - **Messages, @twinly and the API's Mac endpoints need a Mac**, with Twinly open for the API. ### Rules for agents writing code against the API 1. Keep the key on a server or in a script's environment. Never put it in front-end code; the API refuses browser requests. 2. Call `GET /v1/me` first. It is free, and it says whether the owner's Mac is online and which endpoints this key may call. 3. For Mac endpoints, wait with `GET /v1/jobs/{id}?wait=25` in a loop instead of tight polling. 4. On `POST /v1/computer/next-action`, never perform a step with `commits: true` without a person's OK. 5. Verify `X-Twinly-Signature` on the raw webhook body before parsing it. 6. Treat `503 job_unverified` as “maybe created, may still run”: never retry it automatically. There is no job id and no list endpoint, so wait and check for a `job.finished` webhook or the owner's task list before sending again. Do not auto-retry a `POST` on `billing_record_failed` or `billing_unverified` either. 7. Put everything a task needs in its `prompt`; a task run through the API never stops to ask a question. The full reference, with examples in curl, Python and JavaScript, is [Developer API](/docs/developer-api). Questions: [founders@twinly.tech](mailto:founders@twinly.tech). --- ## How Twinly executes URL: https://www.twinly.tech/docs/how-it-executes Twinly does not throw every task at an AI model. It tries cheap, certain steps first and only spends a model on what genuinely needs reasoning. Each layer falls through to the next on a miss, and the whole thing sits on a memory that keeps learning who you are. That is why it gets more reliable the more you use it. ### The idea: cheap and certain first A task drops through several layers, fastest and most certain first, and only reaches the model if nothing simpler can do the job: 1. **Recipe**: a built-in operation for common web actions, with no AI call at all. 2. **Learned pathway**: steps the twin learned from a past run and now replays, with no AI call, and self-healing. 3. **Full AI run**: the twin reasons through a genuinely new task, and its success teaches the layers above. Alongside them sit **the right way in** (how each step reaches an app) and **verification** (how the twin confirms an action happened). Underneath all of it is **memory**, fed into every run. ### Recipes A recipe is a hand-built operation for web actions whose path never changes: a site search through its standard address (Google, Reddit, YouTube, Amazon, eBay, Wikipedia, GitHub, Stack Overflow, Maps) or a direct open (Gmail, Calendar, Drive, LinkedIn, and more). There is nothing to infer, so the twin goes straight there. The matcher only fires when it is confident, so a nuanced request is never hijacked by a recipe that half-fits. And it **composes**: a simple search finishes right there, but if your request has a follow-on (text it to me, save the top five to a sheet), the recipe opens the right page and hands off to the layers below. ### Learned pathways After the twin completes a task by driving your apps, it records the steps as a reusable **pathway**. Anything that came from your wording becomes a fill-in-the-blank, anything secret is dropped, and commit buttons (Post, Send, Buy) are kept exactly as written so the same rules apply on replay. The next time a matching task arrives, the twin replays the saved steps instead of working it out again. - A new pathway is **still learning** until a clean replay proves it **trusted**. - A single failed replay drops it back to learning. - If anything drifts mid-replay, it stops and falls through to a full AI run, which recovers the task and records a corrected version. > Note: Replay never skips your approval rules. Every replayed step passes the same check a fresh run would. ### The right way into each app Every step reaches an app through the best way in it offers: - **An app's own interface first**: Calendar, Reminders and Contacts through the system, PDFs edited directly, apps that can be scripted through their own scripting, and [Connectors](/docs/connectors). - **On websites, the page itself**: the twin lists the page's controls, fills and clicks them by the words they show, and reads each form back after a change. See [Websites and apps](/docs/web-and-apps). - **In other apps, the screen**: a screenshot after every step plus the window's own controls and text, clicked and typed with your real pointer and keyboard. > Warning: Honest limit: text-free graphics (a 3D or CAD viewport, a drawing canvas, a map with no labels) can be out of reach when there is nothing to read or target. Twinly says so rather than guessing a pixel. ### Checking that it worked After a committing action, the twin checks instead of taking the tool's word: a created calendar event is confirmed in your calendar, a Gmail send in your Sent box, a written file on disk with the right content, a text's delivery in Messages. When a check proves something did not happen, the twin tells you (couldn't confirm it went through, check before relying on it). When an action simply cannot be checked, it reports exactly what the tool said. ### The AI model, for the genuinely new When no recipe and no trusted pathway matches, the full agent runs on Twinly's engine (see [Powering your twin](/docs/powering)). Its successful runs feed back up: a new task becomes a learned pathway, so the next similar one is faster and more certain. > Note: A genuinely new task still costs one full AI run the first time. Twinly gets faster and more reliable as it learns you, not on day one. --- ## How Twinly remembers you URL: https://www.twinly.tech/docs/memory Twinly is a clone of you, so it has to know you. It quietly learns your preferences and how you handle things, remembers the tasks it has done for you, anticipates what you will need next, and learns from its own mistakes. It never forgets, and you stay in control of all of it. ### Permanent memory: it learns who you are Under everything sits an always-on memory. The twin learns who you are continuously, not only when you tell it to remember something. Two signals feed it, both captured automatically: - **Your corrections**, the strongest signal. A steer like “never text after 11” or “I prefer the aisle seat” becomes a standing rule the twin follows from then on. - **What your finished tasks reveal**: a stated preference, a named relationship, a vendor you use. Restating something it already knows does not pile up, and what it knows is fed into **every** task. ### It never forgets (on purpose) Memory is **append-only and backed up**: a bad write can never wipe what the twin knows about you. The only thing that clears it is an explicit reset by you. ### Learned pathways: it remembers your tasks Separately from facts about you, the twin remembers **how to do tasks** it has done before, and replays them, as described in [How Twinly executes](/docs/how-it-executes). If a site changes and a saved path stops working, it notices and re-learns a corrected version. You do not have to maintain anything. ### Anticipation: it sees what's coming The twin also looks ahead, locally and quietly, and offers help before you ask, read from your own calendar, mail, and saved notes, never by running an AI in the background: - A meeting soon: it offers a quick prep brief. - Two events overlapping today: it offers to sort the conflict. - Something you were waiting on, now overdue: it offers to draft a polite chase. - A deadline coming up, or a pile of unread mail: it offers a reminder or a catch-up. > Note: Anticipation only ever **suggests**. Suggestions appear calmly on the island, are rate-limited so they never nag, and have a switch to turn them off. Acting on one is always your choice. ### Failure memory: it learns from mistakes When a task hits a real wall (a login wall, a canvas it could not target), the twin remembers what it tried and why it failed, so next time it adapts instead of repeating the same dead end. ### You stay in control - **Everything is local.** Your memory, learned pathways, and failure history live on your device, not in a cloud profile. - **Switches.** Learned pathways and anticipation each have one. - **Reset clears it.** An explicit reset wipes the learned memory; nothing else can. - **Memory never changes your approval rules.** Knowing your preferences makes the twin's drafts better; what waits for your OK is still set by your [approval mode](/docs/approvals). --- ## Privacy & security URL: https://www.twinly.tech/docs/privacy Twinly acts as you, so trust is the whole product. Your private data stays on your device, you choose how much the twin asks before it acts, a few things wait for you no matter what, and you can stop it instantly. ### What stays on your device - **Your messages, contacts, calendar, reminders, photos, and files** are read on your device and never uploaded by Twinly, apart from the parts a task needs, as below. - **Your voice clone** is built and stored on your Mac by Twinly Voice. Your voice is never sent anywhere, and no third-party voice service is used. - **Your learned memory, pathways, and failure history** live on your device. - **Credentials you add** (the Gmail app password, connector sign-ins, a beta build's provider key) are stored in your OS keychain. ### What does leave, and why To reason about a task, the twin sends the engine the relevant context: the request, the draft it is writing, the part of the screen or page it is reading. On the managed Twinly engine that content routes through Twinly's servers to the AI infrastructure providers running the engine's models, only to generate the response, never to train models. On a bring-your-own-key beta build it goes directly to the provider whose key you set. If you sign in, Twinly also processes your account email, subscription and usage records for billing, and, while usage sharing is on, diagnostic telemetry that can include your task prompts; usage sharing switches off in Settings. The precise list is in the [Privacy Policy](/privacy). Email goes directly to Google's servers over an encrypted connection; texts go out through your Messages app; calls go over your Google Voice line. The optional Chrome extension passes only the tab Twinly opened for your task to the Twinly app on the same computer. ### What the twin may do on its own That is your choice: **Ask me every time**, **Ask before it ships**, or **Don't ask** (the default, where each text or email shows for 5 seconds before it goes). In every mode, messages to family, payments over your limit, and the twin's questions wait for you. The modes, the send preview, and undo are explained in [Approvals and control](/docs/approvals). ### What Twinly will not do, in any mode - Type a password, a card number, or an ID number like a Social Security number into a field. - Solve a CAPTCHA or robot check. - Permanently delete your files: anything it removes goes to the Trash. - Act on a request from someone else who texts @twinly. Only your own requests make it act; others get a short reply that shares nothing private about you. - Run an AI in the background watching you. Its suggestions come from reading your own calendar and mail locally. ### How to pause or stop the twin - **Stop a running task** from the island or the app, instantly. - **End a call** from the island at any moment. - **Decline an approval** and nothing goes out. - **Undo** a calendar, reminder or file change for 30 minutes from Already done. - **Turn off** @twinly, learned pathways, or suggestions with their switches. - **Quit the app** and the twin does nothing until you reopen it. --- ## Troubleshooting URL: https://www.twinly.tech/docs/troubleshooting The handful of things that trip people up, with the fix for each. Most trace back to a permission that needs a re-toggle, the call audio bridge, or a site that needs you signed in. ### Common snags and fixes **A permission shows as off (or on but not working) after I granted it** macOS sometimes needs a permission re-toggled, most often right after an app update, which is normal Mac behavior for any app. Open **System Settings ▸ Privacy & Security**, find the permission, turn Twinly **off and back on**, and reopen the app if it asks. The fastest way to check everything at once is **Set everything up** on Twinly's Access screen ([Permissions](/docs/permissions)). **My voice clone sounds robotic** Re-record your clips in a quiet room at your normal pace, record the **full set** rather than the minimum, then click **Build my clone** again on the **Voice** screen. Twinly Voice needs an Apple silicon Mac. Full detail: [Voice & calls](/docs/voice-and-calls). **A website task fails or the twin says it cannot click something** Two common causes. First, the page needs you **signed in**. For your own accounts, install the **Twinly Browser Control** extension so the twin can work in your signed-in Chrome. Second, the target is a **graphics canvas** with nothing to read, like a CAD or 3D viewport, a drawing canvas, or a map with no labels; Twinly says so rather than guessing a pixel. More in [Websites and apps](/docs/web-and-apps). **My phone call has no audio on one side** This is almost always the **BlackHole** audio bridge. Twinly sets Google Voice's **microphone** to **BlackHole 2ch** before each call; if the other side still hears your room, set it in Chrome's site settings for `voice.google.com`. Your Mac's **sound output must stay on your real speakers**, not BlackHole. If BlackHole is not installed, install it from the **Voice** screen. Also confirm **Screen Recording** is granted; that is how the twin hears the other side. Full wiring: [Voice & calls](/docs/voice-and-calls). **Gmail will not connect** You need a **16-character App Password**, not your normal Google password, and **2-Step Verification** must be on (app passwords do not exist without it). Create one at [myaccount.google.com/apppasswords](https://myaccount.google.com/apppasswords) and paste it into **Settings ▸ Gmail**. Some Workspace or Advanced Protection accounts disable app passwords by policy. While Gmail is unavailable the twin falls back to Apple Mail. Setup: [Email (Gmail)](/docs/email). **@twinly does not answer when I text it** Check that **@twinly in Messages** is switched on in **Settings ▸ Messages**; the switch shows what is wrong if something is missing. Twinly needs **Full Disk Access** to see new messages and Messages signed in on the Mac, and the Mac has to be awake with Twinly open. More in [Messages and @twinly](/docs/messages). **Chrome shows a bar saying it started debugging this browser** That is normal while Twinly works in your Chrome through the **Twinly Browser Control** extension. The bar goes away when the task ends, and pressing **Cancel** on it stops the task. **macOS shows a password prompt saying Twinly wants to use the keychain** A key was added outside the app (with the `security` command-line tool), so macOS treats it as not belonging to Twinly and prompts every time. Open **Keychain Access**, delete the Twinly item, then re-enter the key inside the app. Keys the app saves itself never prompt. **The first phone call after launch is slow to speak** The voice engine starts at launch and again when a call becomes likely, so this is rarely noticeable, but the very first line right after opening the app can take a moment. Open Twinly a minute before an important call. **The twin cannot drive Mail or Messages** An **Automation** prompt was declined once, and macOS never re-asks on its own. Open **System Settings ▸ Privacy & Security ▸ Automation**, expand Twinly, and turn on the app it needs to control. Then try the task again. **Computer use stops working with errors about no window** Your screen is **locked** or the display went to sleep. macOS blocks screen access and clicking on a locked Mac, with misleading errors. Unlock it and keep the display awake while the twin works an app on screen. **A Developer API call says twinly_offline** The key owner's Twinly has not checked in for 90 seconds. Open Twinly on that Mac and sign in, accept the Terms if Twinly shows them, keep **Let keys text, call and run tasks as you** on in **Settings ▸ Developer API**, keep the Mac awake, and call `GET /v1/me` to confirm it shows online. More in [Developer API](/docs/developer-api). **The island disappeared from the top of my screen** It was hidden from its own right-click menu. Press `Command + \` to summon it, or click the **Twinly** icon in the menu bar and choose **Message your twin**. ### Still stuck? The **Showcase** screen's readiness panel catches the common misconfigurations with a fix for each. If it is something new, email [founders@twinly.tech](mailto:founders@twinly.tech) with what you tried; we answer.